kexi

KexiStartup.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003-2007 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     if (!args->getOption("host").isEmpty())
00308         cdata.hostName = args->getOption("host");
00309     if (!args->getOption("local-socket").isEmpty())
00310         cdata.localSocketFileName = args->getOption("local-socket");
00311     if (!args->getOption("user").isEmpty())
00312         cdata.userName = args->getOption("user");
00313 //  cdata.password = args->getOption("password");
00314     bool fileDriverSelected;
00315     if (cdata.driverName.isEmpty())
00316         fileDriverSelected = true;
00317     else {
00318         KexiDB::DriverManager dm;
00319         KexiDB::Driver::Info dinfo = dm.driverInfo(cdata.driverName);
00320         if (dinfo.name.isEmpty()) {
00321             //driver name provided explicitly, but not found
00322             KMessageBox::sorry(0, dm.errorMsg());
00323             return false;
00324         }
00325         fileDriverSelected = dinfo.fileBased;
00326     }
00327     bool projectFileExists = false;
00328 
00329     //obfuscate the password, if present
00330 //removed
00331 /*
00332     for (int i=1; i<(argc-1); i++) {
00333         if (qstrcmp("--password",argv[i])==0
00334             || qstrcmp("-password",argv[i])==0)
00335         {
00336             QCString pwd(argv[i+1]);
00337             if (!pwd.isEmpty()) {
00338                 pwd.fill(' ');
00339                 pwd[0]='x';
00340                 qstrcpy(argv[i+1], (const char*)pwd);
00341             }
00342             break;
00343         }
00344     }
00345     */
00346     
00347     const QString portStr = args->getOption("port");
00348     if (!portStr.isEmpty()) {
00349         bool ok;
00350         const int p = portStr.toInt(&ok);
00351         if (ok && p > 0)
00352             cdata.port = p;
00353         else {
00354             KMessageBox::sorry( 0, 
00355                 i18n("You have specified invalid port number \"%1\"."));
00356             return false;
00357         }
00358     }
00359 
00360     m_forcedUserMode = args->isSet("user-mode");
00361     m_forcedDesignMode = args->isSet("design-mode");
00362     m_isProjectNavigatorVisible = args->isSet("show-navigator");
00363     bool createDB = args->isSet("createdb");
00364     const bool alsoOpenDB = args->isSet("create-opendb");
00365     if (alsoOpenDB)
00366         createDB = true;
00367     const bool dropDB = args->isSet("dropdb");
00368     const bool openExisting = !createDB && !dropDB;
00369     const bool readOnly = args->isSet("readonly");
00370     const QString couldnotMsg = QString::fromLatin1("\n")
00371         +i18n("Could not start Kexi application this way.");
00372 
00373     if (createDB && dropDB) {
00374         KMessageBox::sorry( 0, i18n(
00375             "You have used both \"createdb\" and \"dropdb\" startup options.")+couldnotMsg);
00376         return false;
00377     };
00378 
00379     if (createDB || dropDB) {
00380         if (args->count()<1) {
00381             KMessageBox::sorry( 0, i18n("No project name specified.") );
00382             return false;
00383         }
00384         m_action = Exit;
00385     }
00386 
00387 //TODO: add option for non-gui; integrate with KWallet; 
00388 //      move to static KexiProject method
00389     if (!fileDriverSelected && !cdata.driverName.isEmpty() && cdata.password.isEmpty()) {
00390 
00391         if (cdata.password.isEmpty()) {
00392             delete d->passwordDialog;
00393             d->passwordDialog = new KexiDBPasswordDialog(0, cdata, true);
00394 //          connect( d->passwordDialog, SIGNAL(user1Clicked()), 
00395 //              this, SLOT(slotShowConnectionDetails()) );
00396             const int ret = d->passwordDialog->exec();
00397             if (d->passwordDialog->showConnectionDetailsRequested() || ret == QDialog::Accepted) {
00398 //              if ( ret == QDialog::Accepted ) {
00399         //      if (QDialog::Accepted == KPasswordDialog::getPassword(pwd, msg)) {
00400 //moved             cdata.password = QString(pwd);
00401 //              }
00402             } else {
00403                 m_action = Exit;
00404                 return true;
00405             }
00406         }
00407     }
00408 
00409 /*  kdDebug() << "ARGC==" << args->count() << endl;
00410     for (int i=0;i<args->count();i++) {
00411         kdDebug() << "ARG" <<i<< "= " << args->arg(i) <<endl;
00412     }*/
00413 
00414     if (m_forcedUserMode && m_forcedDesignMode) {
00415         KMessageBox::sorry( 0, i18n(
00416         "You have used both \"user-mode\" and \"design-mode\" startup options.")+couldnotMsg);
00417         return false;
00418     }
00419 
00420     //database filenames, shortcut filenames or db names on a server
00421     if (args->count()>=1) {
00422         QString prjName;
00423         QString fileName;
00424         if (fileDriverSelected) {
00425             fileName = QFile::decodeName(args->arg(0));
00426         }
00427         else {
00428             prjName = QString::fromLocal8Bit(args->arg(0));
00429         }
00430         
00431         if (fileDriverSelected) {
00432             QFileInfo finfo(fileName);
00433             prjName = finfo.fileName(); //filename only, to avoid messy names like when Kexi is started with "../../db" arg
00434             cdata.setFileName( finfo.absFilePath() );
00435             projectFileExists = finfo.exists();
00436 
00437             if (dropDB && !projectFileExists) {
00438                 KMessageBox::sorry(0, 
00439                     i18n("Could not remove project.\nThe file \"%1\" does not exist.")
00440                     .arg(QDir::convertSeparators(cdata.dbFileName())));
00441                 return 0;
00442             }
00443         }
00444 
00445         if (createDB) {
00446             if (cdata.driverName.isEmpty())
00447                 cdata.driverName = KexiDB::Driver::defaultFileBasedDriverName();
00448             m_projectData = new KexiProjectData(cdata, prjName); //dummy
00449         }
00450         else {
00451             if (fileDriverSelected) {
00452                 int detectOptions = 0;
00453                 if (fileType=="project")
00454                     detectOptions |= ThisIsAProjectFile;
00455                 else if (fileType=="shortcut")
00456                     detectOptions |= ThisIsAShortcutToAProjectFile;
00457                 else if (fileType=="connection")
00458                     detectOptions |= ThisIsAShortcutToAConnectionData;
00459 
00460                 if (dropDB)
00461                     detectOptions |= DontConvert;
00462 
00463                 QString detectedDriverName;
00464                 const tristate res = detectActionForFile( m_importActionData, detectedDriverName, 
00465                     cdata.driverName, cdata.fileName(), 0, detectOptions );
00466                 if (true != res)
00467                     return res;
00468 
00469                 if (m_importActionData) { //importing action
00470                     m_action = ImportProject;
00471                     return true;
00472                 }
00473 
00474                 //opening action
00475                 cdata.driverName = detectedDriverName;
00476                 if (cdata.driverName=="shortcut") {
00477                     //get information for a shortcut file
00478                     d->shortcutFile = new KexiDBShortcutFile(cdata.fileName());
00479                     m_projectData = new KexiProjectData();
00480                     if (!d->shortcutFile->loadProjectData(*m_projectData, &d->shortcutFileGroupKey)) {
00481                         KMessageBox::sorry(0, i18n("Could not open shortcut file\n\"%1\".")
00482                             .arg(QDir::convertSeparators(cdata.fileName())));
00483                         delete m_projectData;
00484                         m_projectData = 0;
00485                         delete d->shortcutFile;
00486                         d->shortcutFile = 0;
00487                         return false;
00488                     }
00489                     d->connDialog = new KexiDBConnectionDialog(
00490                         *m_projectData, d->shortcutFile->fileName());
00491                     connect(d->connDialog, SIGNAL(saveChanges()), 
00492                         this, SLOT(slotSaveShortcutFileChanges()));
00493                     int res = d->connDialog->exec();
00494                     if (res == QDialog::Accepted) {
00495                         //get (possibly changed) prj data
00496                         *m_projectData = d->connDialog->currentProjectData();
00497                     }
00498 
00499                     delete d->connDialog;
00500                     d->connDialog = 0;
00501                     delete d->shortcutFile;
00502                     d->shortcutFile = 0;
00503 
00504                     if (res == QDialog::Rejected) {
00505                         delete m_projectData;
00506                         m_projectData = 0;
00507                         return cancelled;
00508                     }
00509                 }
00510                 else if (cdata.driverName=="connection") {
00511                     //get information for a connection file
00512                     d->connShortcutFile = new KexiDBConnShortcutFile(cdata.fileName());
00513                     if (!d->connShortcutFile->loadConnectionData(cdata, &d->shortcutFileGroupKey)) {
00514                         KMessageBox::sorry(0, i18n("Could not open connection data file\n\"%1\".")
00515                             .arg(QDir::convertSeparators(cdata.fileName())));
00516                         delete d->connShortcutFile;
00517                         d->connShortcutFile = 0;
00518                         return false;
00519                     }
00520                     bool cancel = false;
00521                     const bool showConnectionDialog = !args->isSet("skip-conn-dialog");
00522                     while (true) {
00523                         if (showConnectionDialog) {
00524                             //show connection dialog, so user can change parameters
00525                             if (!d->connDialog) {
00526                                 d->connDialog = new KexiDBConnectionDialog(
00527                                     cdata, d->connShortcutFile->fileName());
00528                                 connect(d->connDialog, SIGNAL(saveChanges()), 
00529                                     this, SLOT(slotSaveShortcutFileChanges()));
00530                             }
00531                             const int res = d->connDialog->exec();
00532                             if (res == QDialog::Accepted) {
00533                                 //get (possibly changed) prj data
00534                                 cdata = *d->connDialog->currentProjectData().constConnectionData();
00535                             }
00536                             else {
00537                                 cancel = true;
00538                                 break;
00539                             }
00540                         }
00541                         m_projectData = selectProject(&cdata, cancel);
00542                         if (m_projectData || cancel || !showConnectionDialog)
00543                             break;
00544                     }
00545 
00546                     delete d->connShortcutFile;
00547                     d->connShortcutFile = 0;
00548                     delete d->connDialog;
00549                     d->connDialog = 0;
00550 
00551                     if (cancel)
00552                         return cancelled;
00553                 }
00554                 else
00555                     m_projectData = new KexiProjectData(cdata, prjName);
00556             }
00557             else
00558                 m_projectData = new KexiProjectData(cdata, prjName);
00559 
00560         }
00561 //      if (!m_projectData)
00562 //          return false;
00563     }
00564     if (args->count()>1) {
00565         //TODO: KRun another Kexi instances
00566     }
00567 
00568     //let's show connection details, user asked for that in the "password dialog"
00569     if (d->passwordDialog && d->passwordDialog->showConnectionDetailsRequested()) {
00570         d->connDialog = new KexiDBConnectionDialog(*m_projectData);
00571 //      connect(d->connDialog->tabWidget->mainWidget, SIGNAL(saveChanges()), 
00572 //          this, SLOT(slotSaveShortcutFileChanges()));
00573         int res = d->connDialog->exec();
00574 
00575         if (res == QDialog::Accepted) {
00576             //get (possibly changed) prj data
00577             *m_projectData = d->connDialog->currentProjectData();
00578         }
00579 
00580         delete d->connDialog;
00581         d->connDialog = 0;
00582 
00583         if (res == QDialog::Rejected) {
00584             delete m_projectData;
00585             m_projectData = 0;
00586             return cancelled;
00587         }
00588     }
00589 
00590     //---autoopen objects:
00591     const bool atLeastOneAOOFound = getAutoopenObjects(args, "open")
00592         || getAutoopenObjects(args, "design")
00593         || getAutoopenObjects(args, "edittext")
00594         || getAutoopenObjects(args, "execute")
00595         || getAutoopenObjects(args, "new")
00596         || getAutoopenObjects(args, "print")
00597         || getAutoopenObjects(args, "print-preview");
00598 
00599     if (atLeastOneAOOFound && !openExisting) {
00600         KMessageBox::information( 0, 
00601             i18n("You have specified a few database objects to be opened automatically, "
00602                 "using startup options.\n"
00603                 "These options will be ignored because it is not available while creating "
00604                 "or dropping projects."));
00605     }
00606 
00607     if (createDB) {
00608         bool creationNancelled;
00609         KexiGUIMessageHandler gui;
00610         KexiProject *prj = KexiProject::createBlankProject(creationNancelled, projectData(), &gui);
00611         bool ok = prj!=0;
00612         delete prj;
00613         if (creationNancelled)
00614             return cancelled;
00615         if (!alsoOpenDB) {
00616             if (ok) {
00617                 KMessageBox::information( 0, i18n("Project \"%1\" created successfully.")
00618                     .arg( QDir::convertSeparators(projectData()->databaseName()) ));
00619             }
00620             return ok;
00621         }
00622     }
00623     else if (dropDB) {
00624         KexiGUIMessageHandler gui;
00625         tristate res = KexiProject::dropProject(projectData(), &gui, false/*ask*/);
00626         if (res==true)
00627             KMessageBox::information( 0, i18n("Project \"%1\" dropped successfully.")
00628                 .arg( QDir::convertSeparators(projectData()->databaseName()) ));
00629         return res!=false;
00630     }
00631 
00632     //------
00633 
00634 /*  if (m_forcedFinalMode || (m_projectData && projectData->finalMode())) {
00635         //TODO: maybe also auto allow to open objects...
00636         KexiMainWindowImpl::initFinal(m_projectData);
00637         return;
00638     }*/
00639 
00640     if (!m_projectData) {
00641         cdata = KexiDB::ConnectionData(); //clear
00642 
00643         if (args->isSet("skip-startup-dialog") || !KexiStartupDialog::shouldBeShown())
00644             return true;
00645 
00646         if (!d->startupDialog) {
00647             //create d->startupDialog for reuse because it can be used again after conn err.
00648             d->startupDialog = new KexiStartupDialog(
00649                 KexiStartupDialog::Everything, KexiStartupDialog::CheckBoxDoNotShowAgain,
00650                 Kexi::connset(), Kexi::recentProjects(), 0, "KexiStartupDialog");
00651         }
00652         if (d->startupDialog->exec()!=QDialog::Accepted)
00653             return true;
00654 
00655         int r = d->startupDialog->result();
00656         if (r==KexiStartupDialog::TemplateResult) {
00657 //          kdDebug() << "Template key == " << d->startupDialog->selectedTemplateKey() << endl;
00658             QString selectedTemplateKey( d->startupDialog->selectedTemplateKey() );
00659             if (selectedTemplateKey=="blank") {
00660                 m_action = CreateBlankProject;
00661                 return true;
00662             }
00663             else if (selectedTemplateKey=="import") {
00664                 m_action = ImportProject;
00665                 return true;
00666             }
00667             
00669             return true;
00670         }
00671         else if (r==KexiStartupDialog::OpenExistingResult) {
00672 //          kdDebug() << "Existing project --------" << endl;
00673             QString selFile = d->startupDialog->selectedExistingFile();
00674             if (!selFile.isEmpty()) {
00675                 //file-based project
00676 //              kdDebug() << "Project File: " << selFile << endl;
00677                 cdata.setFileName( selFile );
00678                 QString detectedDriverName;
00679                 const tristate res = detectActionForFile( m_importActionData, detectedDriverName, 
00680                     cdata.driverName, selFile );
00681                 if (true != res)
00682                     return res;
00683                 if (m_importActionData) { //importing action
00684                     m_action = ImportProject;
00685                     return true;
00686                 }
00687 
00688                 if (detectedDriverName.isEmpty())
00689                     return false;
00690                 cdata.driverName = detectedDriverName;
00691                 m_projectData = new KexiProjectData(cdata, selFile);
00692             }
00693             else if (d->startupDialog->selectedExistingConnection()) {
00694 //              kdDebug() << "Existing connection: " <<
00695 //                  d->startupDialog->selectedExistingConnection()->serverInfoString() << endl;
00696                 KexiDB::ConnectionData *cdata = d->startupDialog->selectedExistingConnection();
00697                 //ok, now we will try to show projects for this connection to the user
00698                 bool cancelled;
00699                 m_projectData = selectProject( cdata, cancelled );
00700                 if (!m_projectData && !cancelled || cancelled) {
00701                         //try again
00702                         return init(0, 0);
00703                 }
00704                 //not needed anymore
00705                 delete d->startupDialog;
00706                 d->startupDialog = 0;
00707             }
00708         }
00709         else if (r==KexiStartupDialog::OpenRecentResult) {
00710 //          kdDebug() << "Recent project --------" << endl;
00711             const KexiProjectData *data = d->startupDialog->selectedProjectData();
00712             if (data) {
00713 //              kdDebug() << "Selected project: database=" << data->databaseName()
00714 //                  << " connection=" << data->constConnectionData()->serverInfoString() << endl;
00715             }
00717             return data!=0;
00718         }
00719 
00720         if (!m_projectData)
00721             return true;
00722     }
00723     
00724     if (m_projectData && (openExisting || (createDB && alsoOpenDB))) {
00725         m_projectData->setReadOnly( readOnly );
00726         m_action = OpenProject;
00727     }
00728     //show if wasn't show yet
00729 //  importantInfo(true);
00730     
00731     return true;
00732 }
00733 
00734 tristate KexiStartupHandler::detectActionForFile( 
00735     KexiStartupData::Import& detectedImportAction, QString& detectedDriverName,
00736     const QString& _suggestedDriverName, const QString &dbFileName, QWidget *parent, int options )
00737 {
00738     detectedImportAction = KexiStartupData::Import(); //clear
00739     QString suggestedDriverName(_suggestedDriverName); //safe
00740     detectedDriverName = QString::null;
00741     QFileInfo finfo(dbFileName);
00742     if (dbFileName.isEmpty() || !finfo.isReadable()) {
00743         if (!(options & SkipMessages))
00744             KMessageBox::sorry(parent, i18n("<p>Could not open project.</p>")
00745                 +i18n("<p>The file <nobr>\"%1\"</nobr> does not exist or is not readable.</p>")
00746                 .arg(QDir::convertSeparators(dbFileName))
00747                 +i18n("Check the file's permissions and whether it is already opened "
00748                 "and locked by another application."));
00749         return false;
00750     }
00751 
00752     KMimeType::Ptr ptr;
00753     QString mimename;
00754 
00755     const bool thisIsShortcut = (options & ThisIsAShortcutToAProjectFile) 
00756         || (options & ThisIsAShortcutToAConnectionData);
00757 
00758     if ((options & ThisIsAProjectFile) || !thisIsShortcut) {
00759         //try this detection if "project file" mode is forced or no type is forced:
00760         ptr = KMimeType::findByFileContent(dbFileName);
00761         mimename = ptr.data()->name();
00762         kdDebug() << "KexiStartupHandler::detectActionForFile(): found mime is: " 
00763             << mimename << endl;
00764         if (mimename.isEmpty() || mimename=="application/octet-stream" || mimename=="text/plain") {
00765             //try by URL:
00766             ptr = KMimeType::findByURL(dbFileName);
00767             mimename = ptr.data()->name();
00768         }
00769     }
00770     if (mimename.isEmpty() || mimename=="application/octet-stream") {
00771         // perhaps the file is locked
00772         QFile f(dbFileName);
00773         if (!f.open(IO_ReadOnly)) {
00774             // BTW: similar error msg is provided in SQLiteConnection::drv_useDatabase()
00775             if (!(options & SkipMessages))
00776                 KMessageBox::sorry(parent, i18n("<p>Could not open project.</p>")
00777                     +i18n("<p>The file <nobr>\"%1\"</nobr> is not readable.</p>")
00778                     .arg(QDir::convertSeparators(dbFileName))
00779                     +i18n("Check the file's permissions and whether it is already opened "
00780                         "and locked by another application."));
00781             return false;
00782         }
00783     }
00784     if ((options & ThisIsAShortcutToAProjectFile) || mimename=="application/x-kexiproject-shortcut") {
00785         detectedDriverName = "shortcut";
00786         return true;
00787     }
00788 
00789     if ((options & ThisIsAShortcutToAConnectionData) || mimename=="application/x-kexi-connectiondata") {
00790         detectedDriverName = "connection";
00791         return true;
00792     }
00793 
00796     if (ptr.data()) {
00797         if (mimename=="application/x-msaccess") {
00798             if ((options & SkipMessages) || KMessageBox::Yes != KMessageBox::questionYesNo(
00799                 parent, i18n("\"%1\" is an external file of type:\n\"%2\".\n"
00800                 "Do you want to import the file as a Kexi project?")
00801                 .arg(QDir::convertSeparators(dbFileName)).arg(ptr.data()->comment()),
00802                 i18n("Open External File"), KGuiItem(i18n("Import...")), KStdGuiItem::cancel() ) )
00803             {
00804                 return cancelled;
00805             }
00806             detectedImportAction.mimeType = mimename;
00807             detectedImportAction.fileName = dbFileName;
00808             return true;
00809         }
00810     }
00811 
00812     if (!finfo.isWritable()) {
00814     }
00815 
00816     // "application/x-kexiproject-sqlite", etc.:
00817     QString tmpDriverName = Kexi::driverManager().lookupByMime(mimename).latin1();
00818 //@todo What about trying to reuse KOFFICE FILTER CHAINS here?
00819     bool useDetectedDriver = suggestedDriverName.isEmpty() || suggestedDriverName.lower()==detectedDriverName.lower();
00820     if (!useDetectedDriver) {
00821         int res = KMessageBox::Yes;
00822         if (!(options & SkipMessages))
00823             res = KMessageBox::warningYesNoCancel(parent, i18n(
00824              "The project file \"%1\" is recognized as compatible with \"%2\" database driver, "
00825              "while you have asked for \"%3\" database driver to be used.\n"
00826              "Do you want to use \"%4\" database driver?")
00827              .arg(QDir::convertSeparators(dbFileName))
00828              .arg(tmpDriverName).arg(suggestedDriverName).arg(tmpDriverName));
00829         if (KMessageBox::Yes == res)
00830             useDetectedDriver = true;
00831         else if (KMessageBox::Cancel == res)
00832             return cancelled;
00833     }
00834     if (useDetectedDriver) {
00835         detectedDriverName = tmpDriverName;
00836     }
00837     else {//use suggested driver
00838         detectedDriverName = suggestedDriverName;
00839     }
00840 //  kdDebug() << "KexiStartupHandler::detectActionForFile(): driver name: " << detectedDriverName << endl;
00841 //hardcoded for convenience:
00842     const QString newFileFormat = "SQLite3";
00843     if (!(options & DontConvert || options & SkipMessages) 
00844         && detectedDriverName.lower()=="sqlite2" && detectedDriverName.lower()!=suggestedDriverName.lower()
00845         && KMessageBox::Yes == KMessageBox::questionYesNo(parent, i18n(
00846             "Previous version of database file format (\"%1\") is detected in the \"%2\" "
00847             "project file.\nDo you want to convert the project to a new \"%3\" format (recommended)?")
00848             .arg(detectedDriverName).arg(QDir::convertSeparators(dbFileName)).arg(newFileFormat)) )
00849     {
00850 //      SQLite2ToSQLite3Migration *migr = new 
00851         SQLite2ToSQLite3Migration migr( finfo.absFilePath() );
00852         tristate res = migr.run();
00853 //      kdDebug() << "--- migr.run() END ---" <<endl;
00854         if (!res) {
00855             //TODO msg
00856             KMessageBox::sorry(parent, i18n(
00857                 "Failed to convert project file \"%1\" to a new \"%2\" format.\n"
00858                 "The file format remains unchanged.")
00859                 .arg(QDir::convertSeparators(dbFileName)).arg(newFileFormat) );
00860             //continue...
00861         }
00862         if (res==true)
00863             detectedDriverName = newFileFormat;
00864     }
00865 //  action.driverName = detectedDriverName;
00866     if (detectedDriverName.isEmpty()) {
00867         QString possibleProblemsInfoMsg( Kexi::driverManager().possibleProblemsInfoMsg() );
00868         if (!possibleProblemsInfoMsg.isEmpty()) {
00869             possibleProblemsInfoMsg.prepend(QString::fromLatin1("<p>")+i18n("Possible problems:"));
00870             possibleProblemsInfoMsg += QString::fromLatin1("</p>");
00871         }
00872         if (!(options & SkipMessages))
00873             KMessageBox::detailedSorry(parent, 
00874                 i18n( "The file \"%1\" is not recognized as being supported by Kexi.")
00875                     .arg(QDir::convertSeparators(dbFileName)),
00876                 QString::fromLatin1("<p>")
00877                 +i18n("Database driver for this file type not found.\nDetected MIME type: %1")
00878                     .arg(mimename)
00879                 +(ptr.data()->comment().isEmpty() 
00880                     ? QString::fromLatin1(".") : QString::fromLatin1(" (%1).").arg(ptr.data()->comment()))
00881                 +QString::fromLatin1("</p>")
00882                 +possibleProblemsInfoMsg);
00883         return false;
00884     }
00885     return true;
00886 }
00887 
00888 KexiProjectData*
00889 KexiStartupHandler::selectProject(KexiDB::ConnectionData *cdata, bool& cancelled, QWidget *parent)
00890 {
00891     clearStatus();
00892     cancelled = false;
00893     if (!cdata)
00894         return 0;
00895     if (!cdata->savePassword && cdata->password.isEmpty()) {
00896         if (!d->passwordDialog)
00897             d->passwordDialog = new KexiDBPasswordDialog(0, *cdata, false);
00898         const int ret = d->passwordDialog->exec();
00899         if (d->passwordDialog->showConnectionDetailsRequested() || ret == QDialog::Accepted) {
00900 
00901         } else {
00902             cancelled = true;
00903             return 0;
00904         }
00905     }
00906     KexiProjectData* projectData = 0;
00907     //dialog for selecting a project
00908     KexiProjectSelectorDialog prjdlg( parent, "prjdlg", cdata, true, false );
00909     if (!prjdlg.projectSet() || prjdlg.projectSet()->error()) {
00910         KexiGUIMessageHandler msgh;
00911         if (prjdlg.projectSet())
00912             msgh.showErrorMessage(prjdlg.projectSet(), 
00913                 i18n("Could not load list of available projects for <b>%1</b> database server.")
00914                 .arg(cdata->serverInfoString(true)));
00915         else
00916             msgh.showErrorMessage(
00917                 i18n("Could not load list of available projects for <b>%1</b> database server.")
00918                 .arg(cdata->serverInfoString(true)));
00919 //      setStatus(i18n("Could not load list of available projects for database server \"%1\"")
00920 //      .arg(cdata->serverInfoString(true)), prjdlg.projectSet()->errorMsg());
00921         return 0;
00922     }
00923     if (prjdlg.exec()!=QDialog::Accepted) {
00924         cancelled = true;
00925         return 0;
00926     }
00927     if (prjdlg.selectedProjectData()) {
00928         //deep copy
00929         projectData = new KexiProjectData(*prjdlg.selectedProjectData());
00930     }
00931     return projectData;
00932 }
00933 
00934 void KexiStartupHandler::slotSaveShortcutFileChanges()
00935 {
00936     bool ok = true;
00937     if (d->shortcutFile)
00938         ok = d->shortcutFile->saveProjectData(d->connDialog->currentProjectData(), 
00939             d->connDialog->savePasswordOptionSelected(), 
00940             &d->shortcutFileGroupKey );
00941     else if (d->connShortcutFile)
00942         ok = d->connShortcutFile->saveConnectionData(
00943             *d->connDialog->currentProjectData().connectionData(), 
00944             d->connDialog->savePasswordOptionSelected(), 
00945             &d->shortcutFileGroupKey );
00946 
00947     if (!ok) {
00948         KMessageBox::sorry(0, i18n("Failed saving connection data to\n\"%1\" file.")
00949             .arg(QDir::convertSeparators(d->shortcutFile->fileName())));
00950     }
00951 }
00952 
00953 /*void KexiStartupHandler::slotShowConnectionDetails()
00954 {
00955     d->passwordDialog->close();
00956     d->showConnectionDetailsExecuted = true;
00957 }*/
00958 
00959 #include "KexiStartup.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys