Back to index

lightning-sunbird  0.9+nobinonly
nsXInstaller.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code, released
00016  * March 31, 1998.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Samir Gehani <sgehani@netscape.com>
00025  *   Brian Ryner <bryner@brianryner.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nscore.h"
00042 #include "nsXInstaller.h"
00043 #include <gdk/gdkkeysyms.h>
00044 
00045 nsXIContext *gCtx = NULL;
00046 static GtkWidget *sErrDlg = NULL;
00047 
00048 nsXInstaller::nsXInstaller()
00049 {
00050 }
00051 
00052 nsXInstaller::~nsXInstaller()
00053 {
00054   XI_IF_DELETE(gCtx);
00055 }
00056 
00057 int 
00058 nsXInstaller::ParseArgs(int aArgc, char **aArgv)
00059 {
00060   if (aArgc <= 0 || !aArgv)
00061     return E_PARAM;
00062 
00063   for (int argNum = 1; argNum < aArgc; ++argNum) {
00064     /* Print usage */
00065     if (strcmp(aArgv[argNum], "-h") == 0 ||
00066         strcmp(aArgv[argNum], "--help") == 0) {
00067 
00068       if (gCtx->Res("USAGE_MSG"))
00069         fprintf (stderr, gCtx->Res("USAGE_MSG"), aArgv[0], "\n", 
00070                  "\n", "\n", "\n", "\n", "\n", "\n", "\n");
00071       return E_USAGE_SHOWN;
00072     }
00073 
00074     /*
00075      * mode: auto  (show progress UI but assume defaults
00076      *              without user intervention)
00077      */
00078     else if (strcmp(aArgv[argNum], "-ma") == 0) {
00079       gCtx->opt->mMode = nsXIOptions::MODE_AUTO;
00080     }
00081 
00082     /*
00083      * mode: silent (show no UI and have no user intervention)
00084      */
00085     else if (strcmp(aArgv[argNum], "-ms") == 0) {
00086       gCtx->opt->mMode = nsXIOptions::MODE_SILENT;
00087     }
00088 
00089     /*
00090      * ignore [RunAppX] sections
00091      */
00092     else if (strcmp(aArgv[argNum], "-ira") == 0) {
00093       gCtx->opt->mShouldRunApps = FALSE;
00094     }
00095   }
00096 
00097   return OK;
00098 }
00099 
00100 int
00101 nsXInstaller::ParseConfig()
00102 {
00103   int err = OK;
00104   nsINIParser *parser = NULL; 
00105   char *cfg = NULL;
00106 
00107   XI_ERR_BAIL(InitContext());
00108   err = gCtx->LoadResources();
00109   if (err != OK)
00110     return err;
00111 
00112   cfg = nsINIParser::ResolveName(CONFIG);
00113   if (!cfg)
00114     return E_INVALID_PTR;
00115 
00116   parser = new nsINIParser(cfg);
00117   if (!parser) {
00118     err = E_MEM;    
00119     goto BAIL;
00120   }
00121 
00122   err = parser->GetError();
00123   if (err != nsINIParser::OK)
00124     return err;
00125 
00126   XI_ERR_BAIL(ParseGeneral(parser));
00127   XI_ERR_BAIL(gCtx->ldlg->Parse(parser));
00128   XI_ERR_BAIL(gCtx->wdlg->Parse(parser));
00129   //XI_ERR_BAIL(gCtx->cdlg->Parse(parser)); // components before setup type
00130   XI_ERR_BAIL(gCtx->sdlg->Parse(parser));
00131   XI_ERR_BAIL(gCtx->idlg->Parse(parser));
00132 
00133 BAIL:
00134   XI_IF_FREE(cfg);
00135   return err;
00136 }
00137 
00138 int 
00139 nsXInstaller::InitContext()
00140 {
00141   int err = OK;
00142 
00143   gCtx = new nsXIContext();
00144   if (!gCtx)
00145     return E_MEM;
00146 
00147   gCtx->me = this;
00148 
00149   gCtx->ldlg = new nsLicenseDlg();
00150   gCtx->wdlg = new nsWelcomeDlg();
00151   gCtx->sdlg = new nsSetupTypeDlg();
00152   gCtx->cdlg = new nsComponentsDlg();
00153   gCtx->idlg = new nsInstallDlg();
00154 
00155   if (!(gCtx->ldlg && gCtx->wdlg && gCtx->sdlg && gCtx->cdlg && gCtx->idlg)) {
00156     err = E_MEM;
00157     goto BAIL;
00158   }
00159 
00160   return OK;
00161     
00162 BAIL:
00163   XI_IF_DELETE(gCtx->ldlg);
00164   XI_IF_DELETE(gCtx->wdlg);
00165   XI_IF_DELETE(gCtx->sdlg);
00166   XI_IF_DELETE(gCtx->cdlg);
00167   XI_IF_DELETE(gCtx->idlg);
00168   XI_IF_DELETE(gCtx);
00169 
00170   return err;
00171 }
00172 
00173 int 
00174 nsXInstaller::RunWizard(int argc, char **argv)
00175 {
00176   int err = OK;
00177 
00178   XI_VERIFY(gCtx);
00179 
00180   // create the dialog window
00181   if (gCtx->opt->mMode != nsXIOptions::MODE_SILENT) {
00182     gtk_init(&argc, &argv);
00183     gCtx->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
00184     XI_VERIFY(gCtx->window);
00185 
00186     gtk_window_set_position(GTK_WINDOW(gCtx->window), GTK_WIN_POS_CENTER);
00187     gtk_signal_connect(GTK_OBJECT(gCtx->window), "delete_event",
00188                        GTK_SIGNAL_FUNC(Kill), NULL);
00189 
00190     gtk_signal_connect(GTK_OBJECT(gCtx->window), "key_press_event",
00191                        GTK_SIGNAL_FUNC(HandleKeyPress), NULL);
00192 
00193     gtk_window_set_title(GTK_WINDOW(gCtx->window), gCtx->opt->mTitle);
00194 
00195     SetupBoxes();
00196 
00197     // create an area for the header image.
00198     // this will remain 0-height until the user advances past
00199     // the welcome dialog.
00200 
00201     gCtx->header = gtk_event_box_new();
00202     if (gCtx->opt->mHeaderPixmap) {
00203       GdkPixbuf *pb = gdk_pixbuf_new_from_file(gCtx->opt->mHeaderPixmap, NULL);
00204       if (pb) {
00205         GdkPixmap *pm = NULL;
00206         gdk_pixbuf_render_pixmap_and_mask(pb, &pm, NULL, 0);
00207         if (pm) {
00208           GtkStyle *newStyle =
00209             gtk_style_copy(gtk_widget_get_style(gCtx->header));
00210 
00211           newStyle->bg_pixmap[GTK_STATE_NORMAL] = pm;
00212           gtk_widget_set_style(gCtx->header, newStyle);
00213 
00214           // newStyle now owns the pixmap, so we don't unref it.
00215           g_object_unref(newStyle);
00216 
00217           // Make the initial window width be as wide as the header.
00218           gint width, height;
00219           gdk_drawable_get_size(pm, &width, &height);
00220           gtk_window_set_default_size(GTK_WINDOW(gCtx->window), width, -1);
00221         }
00222 
00223         // We're done with rendering the pixbuf, so destroy it.
00224         gdk_pixbuf_unref(pb);
00225       }
00226     }
00227 
00228     // The header contains a vbox with two labels
00229     GtkWidget *header_vbox = gtk_vbox_new(FALSE, 6);
00230     gtk_container_set_border_width(GTK_CONTAINER(header_vbox), 6);
00231 
00232     gCtx->header_title = gtk_label_new(NULL);
00233     gtk_misc_set_alignment(GTK_MISC(gCtx->header_title), 0.0, 0.5);
00234     gtk_box_pack_start(GTK_BOX(header_vbox), gCtx->header_title,
00235                        FALSE, FALSE, 0);
00236 
00237     gCtx->header_subtitle = gtk_label_new(NULL);
00238     gtk_misc_set_alignment(GTK_MISC(gCtx->header_subtitle), 0.0, 0.5);
00239     gtk_misc_set_padding(GTK_MISC(gCtx->header_subtitle), 24, 0);
00240     gtk_box_pack_start(GTK_BOX(header_vbox), gCtx->header_subtitle,
00241                        FALSE, FALSE, 0);
00242 
00243     gtk_container_add(GTK_CONTAINER(gCtx->header), header_vbox);
00244 
00245     gtk_widget_set_size_request(header_vbox, 0, 0);
00246     gtk_box_pack_start(GTK_BOX(gCtx->mainbox), gCtx->header, FALSE, FALSE, 0);
00247 
00248     // create the notebook whose pages are dlgs
00249     gCtx->notebook = gtk_notebook_new();
00250     XI_VERIFY(gCtx->notebook);
00251     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gCtx->notebook), FALSE);
00252     gtk_notebook_set_show_border(GTK_NOTEBOOK(gCtx->notebook), FALSE);
00253     gtk_notebook_set_scrollable(GTK_NOTEBOOK(gCtx->notebook), FALSE);
00254     gtk_widget_show(gCtx->notebook);
00255     gtk_box_pack_start(GTK_BOX(gCtx->mainbox), gCtx->notebook, TRUE, TRUE, 0);
00256 
00257     // create and display the nav buttons
00258     XI_ERR_BAIL(DrawNavButtons());
00259   }
00260 
00261   if (gCtx->opt->mMode == nsXIOptions::MODE_DEFAULT) {
00262     // show welcome dlg
00263     gCtx->wdlg->Show(nsXInstallerDlg::FORWARD_MOVE); 
00264     gtk_widget_show(gCtx->window);
00265 
00266     // pop over to main event loop
00267     gtk_main();
00268   } else {
00269     // show install dlg
00270     if (gCtx->opt->mMode == nsXIOptions::MODE_AUTO)
00271       gCtx->idlg->Show(nsXInstallerDlg::FORWARD_MOVE);
00272 
00273     gCtx->idlg->Next((GtkWidget *)NULL, (gpointer) gCtx->idlg);
00274   }
00275 
00276   return OK;
00277 
00278 BAIL:
00279   return err;
00280 }
00281 
00282 gboolean
00283 nsXInstaller::HandleKeyPress(GtkWidget *widget, GdkEventKey *event,
00284                              gpointer data)
00285 {
00286   if (event->keyval == GDK_Escape) {
00287     gtk_main_quit();
00288     return TRUE;
00289   }
00290 
00291   return FALSE;
00292 }
00293 
00294 gint
00295 nsXInstaller::Kill(GtkWidget *widget, GdkEvent *event, gpointer data)
00296 {
00297   gtk_main_quit();
00298   return FALSE;
00299 }
00300 
00301 void
00302 nsXInstaller::SetupBoxes()
00303 {
00304   GtkWidget *mainbox = gtk_vbox_new(FALSE, 0);
00305   gtk_container_add(GTK_CONTAINER(gCtx->window), mainbox);
00306 
00307   gtk_widget_show(mainbox);
00308   gCtx->mainbox = mainbox; /* mainbox = canvas + nav btns' box */
00309 }
00310 
00311 int
00312 nsXInstaller::DrawNavButtons()
00313 {
00314     int err = OK;
00315     
00316     GtkWidget *navbtnhbox;
00317     GtkWidget *navbtntable;
00318 
00319     XI_VERIFY(gCtx->mainbox);
00320 
00321     gCtx->next = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD);
00322     gCtx->back = gtk_button_new_from_stock(GTK_STOCK_GO_BACK);
00323     gCtx->cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
00324 
00325     gCtx->nextLabel = gtk_label_new(gCtx->Res("NEXT"));
00326     gCtx->backLabel = gtk_label_new(gCtx->Res("BACK"));
00327     XI_VERIFY(gCtx->next);
00328     XI_VERIFY(gCtx->back);
00329     XI_VERIFY(gCtx->cancel);
00330 
00331     gCtx->cancelID = gtk_signal_connect(GTK_OBJECT(gCtx->cancel), "clicked",
00332                                         GTK_SIGNAL_FUNC(Kill), NULL);
00333 
00334     GTK_WIDGET_SET_FLAGS(gCtx->next, GTK_CAN_DEFAULT);
00335 
00336     navbtnhbox = gtk_hbox_new(TRUE, 10);
00337     GtkWidget *separator = gtk_hseparator_new();
00338 
00339     gtk_box_pack_start(GTK_BOX(gCtx->mainbox), separator, FALSE, FALSE, 0);
00340     gtk_box_pack_start(GTK_BOX(gCtx->mainbox), navbtnhbox, FALSE, FALSE, 0); 
00341 
00342     // put a table in the nav btn box
00343     navbtntable = gtk_table_new(1, 5, TRUE);
00344     gtk_box_pack_start(GTK_BOX(navbtnhbox), navbtntable, TRUE, TRUE, 0);
00345 
00346     gtk_table_attach(GTK_TABLE(navbtntable), gCtx->cancel, 2, 3, 0, 1,
00347                      static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND),
00348                      static_cast<GtkAttachOptions>(GTK_SHRINK),
00349                      5, 5);
00350 
00351     gtk_table_attach(GTK_TABLE(navbtntable), gCtx->back, 3, 4, 0, 1, 
00352                      static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND),
00353                      static_cast<GtkAttachOptions>(GTK_SHRINK),
00354                      5, 5);
00355     gtk_table_attach(GTK_TABLE(navbtntable), gCtx->next, 4, 5, 0, 1,
00356                      static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND),
00357                      static_cast<GtkAttachOptions>(GTK_SHRINK),
00358                      5, 5);
00359 
00360     gtk_widget_show_all(gCtx->mainbox);
00361 
00362     return err;
00363 }
00364 
00365 int
00366 nsXInstaller::ParseGeneral(nsINIParser *aParser)
00367 {
00368   int err = OK, size = 0;
00369   char *buf = NULL;
00370  
00371   /* optional: main program name.   if this file exists in the destination
00372      directory, the user will be prompted to remove the directory or choose
00373      a different directory. */
00374 
00375   err = aParser->GetStringAlloc(GENERAL, PROGRAM_NAME, &buf, &size);
00376   if (err == OK && size > 0) {
00377     gCtx->opt->mProgramName = buf;
00378   }
00379 
00380   /* optional: destination directory can be specified in config.ini */
00381   err = aParser->GetStringAlloc(GENERAL, DEFAULT_LOCATION, &buf, &size);
00382   if (err == OK && size > 0) {
00383     /* malloc MAXPATHLEN for consistency in behavior if destination
00384      * directory is not specified in the config.ini
00385      */
00386     gCtx->opt->mDestination = (char *)malloc(MAXPATHLEN * sizeof(char));
00387     strncpy(gCtx->opt->mDestination, buf, size);
00388     XI_IF_FREE(buf);
00389   } else {
00390     err = OK; /* optional so no error if we didn't find it */
00391   }
00392 
00393   /* optional: installer app window title */
00394   size = 0;
00395   buf = NULL;
00396   err = aParser->GetStringAlloc(GENERAL, TITLE, &buf, &size);
00397   if (err == OK && size > 0) {
00398     gCtx->opt->mTitle = buf;
00399   } else {
00400     err = OK; /* optional so no error if we didn't find it */
00401     gCtx->opt->mTitle = strdup(gCtx->Res("DEFAULT_TITLE"));
00402   }
00403 
00404   /* optional: product name (substituted for %s elsewhere) */
00405   size = 0;
00406   buf = NULL;
00407   err = aParser->GetStringAlloc(GENERAL, PRODUCT_NAME, &buf, &size);
00408   if (err == OK && size > 0)
00409     gCtx->opt->mProductName = buf;
00410   else
00411     err = OK;
00412 
00413   /* optional: header image */
00414   size = 0;
00415   buf = NULL;
00416   err = aParser->GetStringAlloc(GENERAL, HEADER_IMAGE, &buf, &size);
00417   if (err == OK && size > 0)
00418     gCtx->opt->mHeaderPixmap = buf;
00419   else
00420     err = OK;
00421 
00422   return err;
00423 }
00424 
00425 int
00426 main(int argc, char **argv)
00427 {
00428   int err = OK;
00429   nsXInstaller *installer = new nsXInstaller();
00430   if (!installer) {
00431     err = E_MEM;
00432     goto out;
00433   }
00434 
00435   if ((err = installer->ParseConfig()) != OK)
00436     goto out;
00437 
00438   if ((err = installer->ParseArgs(argc, argv)) != OK)
00439     goto out;
00440 
00441   err = installer->RunWizard(argc, argv);
00442 
00443  out:
00444   XI_IF_DELETE(installer);
00445   _exit(err);
00446 }
00447 
00448 /*------------------------------------------------------------------*
00449  *   Default Error Handler
00450  *------------------------------------------------------------------*/
00451 int 
00452 ErrorHandler(int aErr, const char* aErrMsg)
00453 {
00454   GtkWidget *okButton, *label;
00455   char msg[256];
00456   char newmsg[256];
00457   char errStr[16];
00458     
00459   sprintf(errStr, "%d", aErr); 
00460   if (!IsErrFatal(aErr)) {
00461        if (aErr == E_INSTALL) {
00462       if (aErrMsg) {
00463               sprintf(newmsg, gCtx->Res(errStr), aErrMsg);
00464               sprintf(msg, gCtx->Res("ERROR"), aErr, newmsg);
00465       }
00466        } else {
00467       sprintf(msg, gCtx->Res("ERROR"), aErr, gCtx->Res(errStr));
00468     }
00469   } else {
00470     sprintf(msg, gCtx->Res("FATAL_ERROR"), aErr, gCtx->Res(errStr));
00471   }
00472     
00473   // lack of gCtx->window indicates we have not yet run RunWizard
00474   // and gtk_init
00475   if (gCtx->opt->mMode == nsXIOptions::MODE_SILENT || !gCtx->window) {
00476     fprintf (stderr, "%s\n", msg);
00477     return aErr;
00478   }
00479 
00480   sErrDlg = gtk_dialog_new();
00481   gtk_window_set_title(GTK_WINDOW(sErrDlg), gCtx->Res("ERROR_TITLE"));
00482   okButton = gtk_button_new_with_label(gCtx->Res("OK_LABEL"));
00483   label = gtk_label_new(msg);
00484 
00485   gtk_window_set_position(GTK_WINDOW(sErrDlg), GTK_WIN_POS_CENTER);
00486   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(sErrDlg)->action_area), okButton);
00487   gtk_signal_connect(GTK_OBJECT(okButton), "clicked",
00488                      GTK_SIGNAL_FUNC(ErrDlgOK), (void*)aErr);
00489 
00490   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(sErrDlg)->vbox), label);
00491 
00492   GTK_WIDGET_SET_FLAGS(okButton, GTK_CAN_DEFAULT);
00493   gtk_widget_grab_default(okButton);
00494   gtk_widget_show_all(sErrDlg);
00495 
00496   gtk_main();
00497 
00498   return aErr;
00499 }
00500 
00501 void
00502 ErrDlgOK(GtkWidget *aWidget, gpointer aData)
00503 {
00504   int err = NS_PTR_TO_INT32(aData);
00505 
00506   if (sErrDlg) {
00507     gtk_widget_destroy(sErrDlg);
00508     sErrDlg = NULL;
00509   }
00510 
00511   gtk_main_quit();
00512 
00513   if (IsErrFatal(err))
00514     exit(err);
00515 }
00516 
00517 int
00518 IsErrFatal(int aErr)
00519 {
00520   int bFatal = TRUE;
00521 
00522   /* non-fatal errors */
00523   switch (aErr) {
00524   case -620:
00525   case -621:
00526   case -624:
00527   case -625:
00528     bFatal = FALSE;
00529   default:
00530     break; 
00531   }
00532 
00533   return bFatal;
00534 }