source: trunk/gsinstaller/gsinstall.cpp@ 2013

Last change on this file since 2013 was 2013, checked in by cs025, 23 years ago

Updates and fixes to permit removal of the main install directory successfully.

  • Property svn:keywords set to Author Date Id Revision
File size: 31.2 KB
Line 
1#include <vector>
2#include <string>
3
4// use the standard namespace
5#if !defined (GSDL_NAMESPACE_BROKEN)
6#if defined(GSDL_USE_OBJECTSPACE)
7using namespace ospace::std;
8#else
9using namespace std;
10#endif
11#endif
12
13#include <stdlib.h>
14#include <stdio.h>
15#include <windows.h>
16#include <commctrl.h>
17
18#include "dirSelector.h"
19#include "common.h"
20#include "configFile.h"
21#include "File.h"
22#include "FilePath.h"
23#include "FileCopier.h"
24#include "launchApp.h"
25#include "unInstall.h"
26
27#include "gsRegistry.h"
28#include "gsPlatform.h"
29#include "gsProgman.h"
30#include "gsProfile.h"
31#include "gsManifest.h"
32
33char *app_cmdLine;
34static char app_name[] = "Greenstone Installer";
35HINSTANCE app_instance;
36HWND app_window;
37
38bool config_complete = false;
39
40dirSelector *rootSelector = NULL;
41
42typedef vector<dirSelector *> dirSelVector;
43
44class GSInstall : public installManager
45{
46private:
47 configureFile *configFile; // the installation configure file
48
49 FilePath *collectPath; // where the collection is currently installed
50 FilePath *installToPath; // the first cut at a destination folder
51 FilePath *sourcePath; // the root source folder
52 FilePath *gsdlSourcePath; // the gsdl folder in the source area
53 FilePath *destinationPath; // where the executables will be installed to
54 FilePath *dataDestPath; // where the collection data files will go
55
56 bool installVolume; // whether to install collection data
57 bool installFullVolume; // whether to install all collection data
58
59 dirSelVector selectedDirs; // configuration objects
60
61 gsPlatform platform; // platform information
62 gsRegistry *gsRegister; // registry
63 gsManifest *manifest; // manifest of files
64 gsProgramManager *progman; // program manager connection
65
66 void getSourcePath();
67 void getExistingInstall();
68public:
69 bool installExe;
70
71 GSInstall(bool uninstall);
72 FilePath *collectionPath();
73 FilePath *installPath();
74 FilePath *installSourcePath() { return this->sourcePath; }
75 bool copyFiles();
76 bool updateRegistry();
77 bool updateProfiles();
78 bool updateProgman();
79 bool updateSetupExe();
80 bool installNetscape();
81 bool removeFailed(string file) { return this->manifest->undoFailed(file); }
82 void setDestination();
83 bool setUninstall();
84 void uninstall();
85 void setManifest();
86 void addSelectedDir(dirSelector *selector);
87};
88
89static string gsdl_DBHOME = "gdbmhome";
90static string gsdl_VLHOME = "gsdlhome";
91static string gsdl_STATIC = "staticpath";
92static string gsdl_COLLIST= "collections";
93
94void gsInstall_getDesktopExt(int *sx, int *sy)
95{
96 HDC dc = GetDC(GetDesktopWindow());
97 *sx = GetDeviceCaps(dc, HORZRES);
98 *sy = GetDeviceCaps(dc, VERTRES);
99 ReleaseDC(GetDesktopWindow(), dc);
100}
101
102/**
103 * Get various and nefarious details before we start trying an installation.
104 * The installation configuration file is read, we seek for an existing
105 * installation and reconfigure the default install settings appropriately.
106 * THe source path is also discovered.
107 */
108GSInstall::GSInstall(bool uninstall)
109 : installManager()
110{
111 // get the installation configuration file
112 this->configFile = new configureFile("install.cfg");
113
114 // set up "default" installation bits
115 this->installToPath = new FilePath("C:\\GSDL");
116 this->collectPath = NULL;
117 this->installExe = true; // we must install the exe files
118
119 // don't attempt this with windows 3.1
120 if (!this->platform.isWindows32s())
121 {
122 // get the registry
123 this->gsRegister = new gsRegistry(*this, *this->configFile);
124 }
125 else
126 {
127 // TODO: in windows 3.1 we can't use the register as it isn't fully/properly
128 // implemented; we must get the existing path information from elsewhere
129 this->gsRegister = new gsRegistry(*this, *this->configFile);
130 }
131
132 // assume for now we will install the full volume and the database
133 this->installVolume = true;
134 this->installFullVolume = true;
135
136 // detect any existing installation; unnecessary in uninstall (it's the
137 // "source" directory - see later
138 if (uninstall == false)
139 {
140 this->getExistingInstall();
141 }
142
143 // get where we are installing from
144 this->getSourcePath();
145
146 // if doing an uninstall we now know where the "uninstall" is
147 if (uninstall == true)
148 {
149 this->collectPath = this->sourcePath;
150 }
151
152 // get the manifest and program manager objects
153 if (uninstall)
154 {
155 this->manifest = new gsManifest(*this);
156 }
157 else
158 {
159 this->manifest = new gsManifest(*this, *this->sourcePath);
160 }
161 this->progman = new gsProgramManager(*this);
162
163 // tell manifest the number of bits in the raw windows environment; this may
164 // be required for loading certain modules such as winsock; the number of
165 // macros is over-the-top, but is more robust to unforeseen future requirements
166 /* if (platform.isWindows32s())
167 { this->manifest->expandMacro("WINBITS", "16");
168 this->manifest->expandMacro("WIN16", "16");
169 this->manifest->expandMacro("WIN32", "32");
170 }
171 else
172 { this->manifest->expandMacro("WINBITS", "32");
173 this->manifest->expandMacro("WIN16", "");
174 this->manifest->expandMacro("WIN32", "32");
175 }
176 */
177 // inform manifest of collection directory name
178 string colDir;
179 colDir = this->configFile->getString("CollectionDirName");
180 if (colDir != "")
181 {
182 this->manifest->expandMacro("COLDIRNAME", colDir);
183 }
184}
185
186/**
187 * Detect the presence of an existing installation
188 */
189void GSInstall::getExistingInstall()
190{
191 if (!this->platform.isWindows32s())
192 {
193 if (gsRegister->collectionInstalled())
194 {
195 // TODO: check if receptionist etc should be replaced or not
196
197 // TODO: check build version of the volume
198
199 // Get location of the library executable from the registry; this
200 // is thence used to get the location of gsdl.ini etc.
201 this->collectPath = this->gsRegister->collectionPath();
202 if (this->collectPath->pathString() != "")
203 {
204 this->installToPath = this->collectPath;
205 // we don't need to install the exe files
206 this->installExe = false;
207 }
208 }
209 }
210}
211
212/**
213 * get the source path for this install; we derive this from the path of this
214 * executable (the installer)
215 */
216void GSInstall::getSourcePath()
217{
218 static char filename[512];
219 FilePath *exePath;
220
221 // get the filepath of this executable
222 GetModuleFileName(0, filename, 512);
223 exePath = new FilePath(filename);
224
225 // get the parent (i.e. the folder containing this executable) for the source
226 // folder
227 this->sourcePath = exePath->parent();
228
229 // get the gsdl source path
230 this->gsdlSourcePath = new FilePath(this->sourcePath->pathString(), "gsdl");
231 delete exePath;
232}
233
234/**
235 * Controls the actual copying of the files; manifests of what to install
236 * are read in from a configuration on the hard drive - this should be basically
237 * greenstone dependent, and void of any collection-specific rubbish!
238 */
239bool GSInstall::copyFiles()
240{
241 // ensure that we have got the required destination and that we can write there
242 if (this->installExe)
243 {
244// this->destinationPath->ensureWriteablePath();
245 }
246
247 // ensure that the destination path for the collection itself is available
248 if (this->installVolume)
249 {
250// this->dataDestPath->ensureWriteablePath();
251 }
252
253 // do the copy
254 this->manifest->copy(this->gsdlSourcePath);
255
256 return true;
257}
258
259/**
260 * Fulfill registry update requirements; first the keys are set up, then they
261 * are filled with key item/value pairs.
262 */
263bool GSInstall::updateRegistry()
264{
265 // don't do registry stuff under windows 32s
266 // TODO: actually reinstate what is required
267 if (this->platform.isWindows32s())
268 {
269 return true;
270 }
271
272 // ensure all pertinent keys already exist for this volume/collection
273 this->gsRegister->ensureKeysExist();
274
275 // now move on to add key items
276 if (this->installExe)
277 {
278 // FilePath *serverPath = new FilePath(this->destinationPath->pathString(), "server.exe");
279 FilePath *serverPath = new FilePath(this->destinationPath->pathString(), "server.exe");
280 FilePath *setupPath = new FilePath(this->destinationPath->pathString(), "gssetup.exe");
281 FilePath *logPath = new FilePath(this->destinationPath->pathString(), "install.log");
282 string exeKeyPath, uninstallCmd, uninstallKeyPath;
283 gsPlatform platform;
284
285 // store the normal collection key information
286 this->gsRegister->storeKeyString(HKEY_LOCAL_MACHINE,
287 this->gsRegister->collectKeyId(),
288 "library",
289 serverPath->pathString());
290
291 // This test is in fact for 9x or NT 4+; we're worried about the registry
292 // format, not about the shell as such
293 if (platform.isExplorerShell())
294 {
295 // get special app path key for windows 9x
296 exeKeyPath = this->gsRegister->exeKeyId("server.exe");
297
298 // ensure that the exe key exists
299 this->gsRegister->ensureKeyExists(HKEY_LOCAL_MACHINE, exeKeyPath);
300
301 // store default key for this application
302 this->gsRegister->storeKeyString(HKEY_LOCAL_MACHINE,
303 exeKeyPath,
304 "",
305 serverPath->pathString());
306
307 // store path for this application
308 this->gsRegister->storeKeyString(HKEY_LOCAL_MACHINE,
309 exeKeyPath,
310 "path",
311 this->destinationPath->pathString());
312
313 // create uninstall command line
314 uninstallCmd = setupPath->pathString() + " -u " + logPath->pathString();
315 uninstallKeyPath = this->gsRegister->uninstallKeyId(this->configFile->getString("CollectionName"));
316
317 // ensure uninstall key exists
318 this->gsRegister->ensureKeyExists(HKEY_LOCAL_MACHINE, uninstallKeyPath);
319
320 this->gsRegister->storeKeyString(HKEY_LOCAL_MACHINE, uninstallKeyPath,
321 "DisplayName",
322 this->configFile->getString("CollectionName"));
323 this->gsRegister->storeKeyString(HKEY_LOCAL_MACHINE, uninstallKeyPath,
324 "UninstallString", uninstallCmd);
325 }
326 delete setupPath;
327 delete serverPath;
328 delete logPath;
329 }
330
331 if (this->installVolume || this->installFullVolume)
332 {
333 // no actual key items are held in the volume key; for future use
334 }
335 return true;
336}
337
338/**
339 * Set the destination directory details
340 */
341void GSInstall::setDestination()
342{
343 // get configuration from the install wizard pages
344 this->destinationPath = new FilePath(this->selectedDirs[0]->selectedPath());
345 this->installFullVolume = this->selectedDirs[1]->getOption();
346 if (this->installFullVolume || true) // NB: always take path from 2nd dialog
347 {
348 this->dataDestPath = new FilePath(this->selectedDirs[1]->selectedPath());
349 }
350 else
351 {
352 this->dataDestPath = this->destinationPath;
353 }
354
355
356 // set the log for writing
357
358 FilePath *logPath = new FilePath(this->destinationPath->pathString(), "install.log");
359
360 this->setLogFile(logPath->pathString());
361
362 this->readLog();
363
364 delete logPath;
365
366}
367
368bool GSInstall::setUninstall()
369{
370 char tempPathStr[MAX_PATH];
371
372 // if we failed to get the Windows temporary directory, abort
373 if (GetTempPath(MAX_PATH, tempPathStr) == 0)
374 {
375 // TODO: abort!
376 return false;
377 }
378
379 FilePath tempPath(tempPathStr);
380
381 // if we are not running in the Windows temporary directory, copy this exe
382 // and the installation log to the temporary directory and run them there
383 if (*this->sourcePath != tempPath)
384 {
385 // copy this and install.log to temporary directory and restart
386 FilePath exePath(*this->sourcePath, "gssetup.exe");
387 FilePath logPath(*this->sourcePath, "install.log");
388
389 FilePath exeDest(tempPath, "gssetup.exe");
390 FilePath logDest(tempPath, "install.log");
391
392 CopyFile(exePath.cString(), exeDest.cString(), false);
393 CopyFile(logPath.cString(), logDest.cString(), false);
394
395 // now run the gssetup in the temporary directory
396 launchApp launchUninstall(exeDest);
397 launchUninstall.setCommandLine(" -u " + logPath.pathString());
398 launchUninstall.run(false, 0, "", "", false);
399
400 return false;
401 }
402
403 // open the log for reading from the directory this exe is now in (which
404 // will in fact be the temporary directory as outlined above)
405 FilePath *logPath = new FilePath(this->sourcePath->pathString(), "install.log");
406
407 this->setLogFile(logPath->pathString());
408 this->readLog();
409 delete logPath;
410
411 return true;
412}
413
414void GSInstall::uninstall()
415{
416 FilePath *iniPath;
417 string command;
418 stringArray params;
419
420 this->manifest = new gsManifest(*this); // get a manifest manager
421
422 iniPath = new FilePath(this->collectPath->pathString(), "gsdl.ini");
423 gsProfile gsdlProfile(*this, iniPath->pathString());
424
425 while ((command = this->popCommand(params)) != "")
426 {
427 if (!this->manifest->undoAction(command, params))
428 {
429 if (!gsdlProfile.undoAction(command, params))
430 {
431 if (!this->gsRegister->undoAction(command, params))
432 {
433 if (!this->progman->undoAction(command, params))
434 {
435 }
436 }
437 }
438 }
439 }
440
441}
442
443/**
444 * Collate the manifest for installation
445 */
446void GSInstall::setManifest()
447{
448 if (this->installExe)
449 {
450 this->manifest->selectGroup("library", *this->destinationPath);
451 }
452 if (this->installFullVolume)
453 {
454 this->manifest->selectGroup("collection", *this->dataDestPath);
455 }
456 else
457 {
458 this->manifest->selectGroup("database", *this->dataDestPath);
459 }
460}
461
462/**
463 * Update the program manager/explorer shell with icons, groups etc for this
464 * collection/application
465 */
466bool GSInstall::updateProgman()
467{
468 string groupName;
469 string collectionName;
470
471 // if we managed to get a connection to the program manager (or explorer
472 // shell) then do the requisite updates
473 if (this->progman->connect())
474 {
475 // get group name from folders
476 groupName = this->configFile->getString("ProgramGroupName");
477 if (groupName == "")
478 {
479 // TODO: error handling
480 this->progman->disconnect();
481 return false;
482 }
483 collectionName = this->configFile->getString("CollectionName");
484 if (collectionName == "")
485 {
486 // TODO: error handling
487 this->progman->disconnect();
488 return false;
489 }
490
491 // add the group
492 if (!this->progman->addProgramGroup(groupName))
493 {
494 this->progman->disconnect();
495 return false;
496 }
497
498 // add a "server" icon
499 FilePath libraryPath(this->destinationPath->pathString(), "server.exe");
500 if (!this->progman->addIcon(groupName, collectionName, libraryPath.pathString(), "", ""))
501 {
502 }
503
504 FilePath readMePath(this->destinationPath->pathString(), "readme.txt");
505 if (!this->progman->addIcon(groupName, "ReadMe", readMePath.pathString(), "", ""))
506 {
507 }
508
509 FilePath supportPath(this->destinationPath->pathString(), "Support.htm");
510 if (!this->progman->addIcon(groupName, "Technical Support", supportPath.pathString(), "", ""))
511 {
512 }
513
514
515 FilePath uninstallPath(this->destinationPath->pathString(), "gssetup.exe");
516 FilePath logPath(this->destinationPath->pathString(), "install.log");
517 if (this->platform.isWindows32s() == false) // NB: don't add this shortcut under
518 // Windows32s as it causes a crash
519 //
520 // TODO: reinstate this with Win3.1
521 // happy code
522 {
523 if (!this->progman->addIcon(groupName, "Uninstall", uninstallPath.pathString(), " -u " + logPath.pathString(),
524 "Remove this Greenstone collection"))
525 {
526 }
527 }
528 else
529 { MessageBox(0, uninstallPath.cString(), logPath.cString(), MB_OK);
530 }
531 // disconnect from program manager
532 this->progman->disconnect();
533 }
534 return true;
535}
536
537/**
538 * Update the profile (ini) files associated with the program; these changes
539 * must be made after copying to files in the destination/executable folders
540 * as we can't write to CD-ROM (or at least we shouldn't - the install would
541 * become tainted).
542 */
543bool GSInstall::updateProfiles()
544{
545 FilePath *exePath;
546 FilePath *dataPath;
547 FilePath *iniPath;
548
549 // if we're installing the exe, then the exe path leads to the destination
550 // folder of this installation sequence; if not, then we pick it up from
551 // the existing location of that folder
552 if (this->installExe)
553 {
554 exePath = this->destinationPath;
555 }
556 else
557 {
558 exePath = this->collectPath;
559 }
560 iniPath = new FilePath(exePath->pathString(), "gsdl.ini");
561
562 // TODO: check if this 'if' structure is correct; suspect that it isn't quite
563 // as we may not be installing the volume, but exe etc individually;
564 // may be okay now but not future proof; also the destination directory
565 // may or may not be tied to the exe directory if that existed already.
566 // This all needs investigating
567 if (this->installVolume)
568 {
569 // Manufacture the appropriate section name now; an equivalent string
570 // is also constructed
571 string volumeSectionString = this->configFile->getString("CollectionName") +
572 "#" + this->configFile->getString("CollectionVolume");
573
574 // if we're installing the full data for the collection, then take the
575 // data path to be at the selected destination directory for the collection
576 // files; otherwise take the data path to be on the CD/distribution media
577 if (this->installFullVolume)
578 {
579 dataPath = this->dataDestPath;
580 }
581 else
582 {
583 dataPath = this->gsdlSourcePath;
584 }
585
586 // create a profile object to write to the gsdl.ini file
587 gsProfile gsdlProfile(*this, iniPath->pathString());
588
589 // if installing the executable, add the database and greenstone home
590 // directories
591 if (this->installExe)
592 {
593 // set the correct exe entries in gsdl.ini (in the gsdl section)
594 gsdlProfile.writeString("gsdl", "gsdlhome", dataPath->pathString());
595 gsdlProfile.writeString("gsdl", "gdbmhome", this->dataDestPath->pathString());
596 }
597
598 // set the correct collection volume entries in gsdl.ini
599 if (this->installVolume)
600 {
601 // note the collection in the general gsdl section; and add the undo item too
602 gsdlProfile.ensureListMember("gsdl", gsdl_COLLIST, volumeSectionString);
603
604 // note the volume data in its own section
605 gsdlProfile.writeString(volumeSectionString, "gsdlhome", dataPath->pathString());
606 gsdlProfile.writeString(volumeSectionString, "gdbmhome", this->dataDestPath->pathString());
607 gsdlProfile.writeString(volumeSectionString, "staticpath", dataPath->pathString());
608 }
609 }
610
611 // clean up
612 delete iniPath;
613
614 return true;
615}
616
617/**
618 * Ensure that we've installed the very latest setup executable @ the destination/
619 * greenstone folder
620 */
621bool GSInstall::updateSetupExe()
622{
623 FilePath *destExePath;
624 FilePath srcExeFile(*this->sourcePath, "gssetup.exe");
625
626 if (this->installExe)
627 {
628 destExePath = this->destinationPath;
629 }
630 else
631 {
632 destExePath = this->collectPath;
633 }
634 FilePath destExeFile(*destExePath, "gssetup.exe");
635
636 if (this->platform.isWindows32s() == false) // NB: Don't copy the setup executable
637 // to Windows32s as it causes a hang!
638 { if (!CopyFile(srcExeFile.cString(), destExeFile.cString(), false))
639 {
640 return false;
641 }
642 }
643 return true;
644}
645
646bool GSInstall::installNetscape()
647{
648 FilePath netscape32Path(this->sourcePath->pathString(), "netscape\\n32e405.exe");
649 FilePath netscape16Path(this->sourcePath->pathString(), "netscape\\n16e405.exe");
650
651 launchApp launchNetscape(netscape32Path);
652 launchNetscape.platformApp(gsPlatform_WINDOWS32S, netscape16Path);
653 launchNetscape.platformApp(gsPlatform_WINDOWS, netscape16Path);
654 if (launchNetscape.run(true, 1,
655 "This library is displayed using a web browser. If you don't "
656 "currently have a web browser installed on your machine you may "
657 "install the Netscape 4.05 browser now.\n\n"
658 "Note that if your current browser was provided by your internet "
659 "service provider, you should install Netscape in a different "
660 "directory.\n\n"
661 "Would you like to install the Netscape 4.05 browser now?",
662 "Greenstone Installer", false) < 0)
663 {
664 // MessageBox(0, "Error", app_name, MB_OK);
665 return false;
666 }
667 return true;
668}
669
670FilePath *GSInstall::collectionPath()
671{
672 return this->collectPath;
673}
674
675FilePath *GSInstall::installPath()
676{
677 return this->installToPath;
678}
679
680void GSInstall::addSelectedDir(dirSelector *selector)
681{
682 this->selectedDirs.push_back(selector);
683}
684
685typedef struct
686{
687 dirSelector * dirSelect;
688} GSInstall_dirPathData;
689
690HGLOBAL loadDirBrowser()
691{
692 HRSRC resHdl;
693
694 resHdl = FindResource(app_instance, "MySaveAsDlg", RT_DIALOG);
695 return LoadResource(app_instance, resHdl);
696}
697
698void gsInstall_wizard_centre(HWND dialog)
699{
700 RECT rect;
701 int sx, sy;
702 int dx, dy;
703
704 gsInstall_getDesktopExt(&sx, &sy);
705 GetWindowRect(dialog, &rect);
706
707 dx = (sx - rect.right + rect.left) >> 1;
708 dy = (sy - rect.bottom + rect.top) >> 1;
709 SetWindowPos(dialog, 0, dx, dy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
710}
711
712BOOL FAR PASCAL GSInstall_dlgproc(HWND Dialog, UINT Message, WPARAM wParam, LPARAM lParam)
713{
714 static bool first = true; // static variable to hack the position of the
715 // whole wizard; note doing this in the wizard
716 // property sheet initialisation DOES NOT WORK
717
718 switch (Message)
719 {
720 case WM_INITDIALOG:
721 {
722 dirSelector *selector = (dirSelector *) ((PROPSHEETPAGE *) lParam)->lParam;
723
724 SetDlgItemText(Dialog, dirpath_PROMPT, selector->prompt);
725 if (selector->optPrompt != NULL)
726 {
727 SetDlgItemText(Dialog, dirpath_OPTION, selector->optPrompt);
728 EnableWindow(GetDlgItem(Dialog, dirpath_BROWSE), true); // was false
729 EnableWindow(GetDlgItem(Dialog, dirpath_PATH), true); // was false
730 }
731 else
732 {
733 ShowWindow(GetDlgItem(Dialog, dirpath_OPTION), SW_HIDE);
734 }
735 SetWindowLong(Dialog, GWL_USERDATA, ((PROPSHEETPAGE *) lParam)->lParam);
736 SetDlgItemText(Dialog, dirpath_PATH, selector->selectedPath());
737
738 // if this is the first time this function is called, then centre the
739 // wizard into the centre of our screen.
740 if (first)
741 {
742 gsInstall_wizard_centre(GetParent(Dialog));
743 first = false;
744 }
745 }
746 return TRUE;
747
748 case WM_COMMAND:
749 switch (LOWORD(wParam))
750 {
751 case dirpath_BROWSE:
752 ((dirSelector *) GetWindowLong(Dialog, GWL_USERDATA))->requestPath(Dialog, "Select Directory");
753 SetDlgItemText(Dialog, dirpath_PATH, ((dirSelector *) GetWindowLong(Dialog, GWL_USERDATA))->selectedPath());
754 break;
755
756 case dirpath_OPTION:
757 ((dirSelector *) GetWindowLong(Dialog, GWL_USERDATA))->setOption(IsDlgButtonChecked(Dialog, dirpath_OPTION) == BST_CHECKED);
758 if (HIWORD(wParam) == BN_CLICKED && false)
759 // don't do the enable/disable these days
760 {
761 EnableWindow(GetDlgItem(Dialog, dirpath_BROWSE), IsDlgButtonChecked(Dialog, dirpath_OPTION));
762 EnableWindow(GetDlgItem(Dialog, dirpath_PATH), IsDlgButtonChecked(Dialog, dirpath_OPTION));
763 }
764 break;
765 }
766 break;
767
768 case WM_NOTIFY:
769 switch (((LPNMHDR) lParam)->code)
770 {
771 case PSN_SETACTIVE:
772 {
773 dirSelector *selector = ((dirSelector *) GetWindowLong(Dialog, GWL_USERDATA));
774
775 // bodge to set the dialogue path to the correct value
776 if (selector != rootSelector)
777 {
778 selector->setPath(rootSelector->selectedPath());
779 SetDlgItemText(Dialog, dirpath_PATH, selector->selectedPath());
780 }
781
782 if (selector->isFinal())
783 {
784 PropSheet_SetWizButtons(GetParent(Dialog), PSWIZB_BACK | PSWIZB_FINISH);
785 }
786 else
787 {
788 PropSheet_SetWizButtons(GetParent(Dialog), PSWIZB_BACK | PSWIZB_NEXT);
789 }
790 }
791 break;
792
793 case PSN_KILLACTIVE:
794 break;
795
796 case PSN_WIZNEXT:
797 {
798 // note the path from the dialog
799 dirSelector *selector = ((dirSelector *) GetWindowLong(Dialog, GWL_USERDATA));
800 selector->setPathFromEdit(GetDlgItem(Dialog, dirpath_PATH));
801
802 // create a new FilePath object to check on the proposed destination
803 FilePath *path = new FilePath(selector->selectedPath());
804
805 // if the proposed destination doesn't exist, ask the user whether to
806 // create it now. TODO: actually create it if asked!
807 if (path->exists() == false)
808 {
809 char buffer[128];
810
811 sprintf(buffer, "Directory %s does not exist - create it?", selector->selectedPath());
812
813 // if the user cancelled that, then don't permit them to go to the next
814 // page of the wizard
815 if (MessageBox(0, buffer, app_name, MB_YESNO | MB_TOPMOST) == IDNO)
816 {
817 delete path;
818 SetWindowLong(Dialog, DWL_MSGRESULT, TRUE);
819 return TRUE;
820 }
821 }
822 delete path;
823 }
824 break;
825
826 case PSN_WIZFINISH:
827 {
828 // Finish the activity now
829 config_complete = true;
830 }
831 break;
832 }
833 break;
834 }
835 return FALSE;
836}
837
838void GSInstall_init_propertySheet(PROPSHEETPAGE &ppage, char *prompt, char *optPrompt,
839 char *title, GSInstall &installer, bool isFinal)
840{
841 GSInstall_dirPathData *data = new GSInstall_dirPathData;
842
843 // create the directory Selector
844 data->dirSelect = new dirSelector(prompt, optPrompt, title, installer.installPath());
845 data->dirSelect->setFinal(isFinal);
846
847 // if there is no extant root selector, make this it
848 if (rootSelector == NULL)
849 {
850 rootSelector = data->dirSelect;
851 }
852
853 // add this directory selector to the installer
854 installer.addSelectedDir(data->dirSelect);
855
856 // set up the rest of the page
857 ppage.dwSize = sizeof(PROPSHEETPAGE);
858 ppage.dwFlags = PSP_USETITLE;
859 ppage.hInstance = app_instance;
860 ppage.pszTemplate = "getDirPath";
861 ppage.pszIcon = 0;
862 ppage.pszTitle = app_name;
863 ppage.pfnDlgProc = (DLGPROC) GSInstall_dlgproc;
864 ppage.lParam = (LPARAM) data->dirSelect;
865 ppage.pfnCallback = NULL;
866}
867
868int CALLBACK GSInstall_wizardProc(HWND dialog, UINT Message, LPARAM lParameter)
869{
870 switch (Message)
871 {
872 case PSCB_INITIALIZED :
873 // Process PSCB_INITIALIZED
874 gsInstall_wizard_centre(dialog);
875 break ;
876
877 case PSCB_PRECREATE :
878 // Process PSCB_PRECREATE
879 break ;
880
881 default :
882 // Unknown message
883 break ;
884 }
885 return 0;
886}
887
888bool GSInstall_init_wizard(GSInstall &install)
889{
890 PROPSHEETHEADER pshead;
891 PROPSHEETPAGE ppage[2];
892 bool reply;
893
894 ZeroMemory(&pshead, sizeof(PROPSHEETHEADER));
895 pshead.dwSize = sizeof(PROPSHEETHEADER);
896 pshead.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK | PSH_USEHICON | PSH_WIZARD;
897 pshead.hwndParent = app_window;
898 pshead.hInstance = app_instance;
899 pshead.hIcon = NULL;
900 pshead.pszCaption = "Greenstone Installer";
901 pshead.nPages = 2;
902 pshead.nStartPage = 0;
903 if (install.installExe == true)
904 {
905 pshead.ppsp = ppage;
906 }
907 else // cheat as the executable etc. is already installed
908 {
909 pshead.ppsp = &ppage[1];
910 }
911 pshead.pfnCallback = GSInstall_wizardProc;
912
913 GSInstall_init_propertySheet( ppage[0],
914 "Choose a directory to install your "
915 "GreenStone software to.", NULL,
916 "Select", install, false);
917 GSInstall_init_propertySheet(ppage[1],
918 "Choose a directory to install the collection "
919 "files to.\n\n"
920 "You can also choose to install all the collection "
921 "files onto your computer hard drive.\n\n"
922 "If you do this, you will not have to load the "
923 "CD-ROM each time you use your Greenstone software "
924 "but more of your hard disk will be used.\n\n"
925 "Some collection files must be installed on your "
926 "computer.\n\n",
927 "Install all collection files",
928 "Select", install, true);
929
930 reply = (PropertySheet (&pshead) >= 0);
931 return reply;
932}
933
934long FAR PASCAL GSInstallWindProc(HWND Window, WORD Message, WPARAM wParameter, LPARAM lParameter)
935{
936 long reply = 0;
937
938 switch(Message)
939 {
940 case WM_CREATE:
941 {
942 ShowWindow(Window, SW_MAXIMIZE);
943 }
944 break;
945
946 case WM_COMMAND:
947 break;
948
949 case WM_USER:
950 {
951 if (strstr(app_cmdLine, "-u") != NULL)
952 {
953 // uninstall action
954
955 // skip past the -u option itself
956 char *at = strstr(app_cmdLine, "-u");
957 at += strlen("-u");
958
959 // extract the log file path from the command line
960 while (*at == ' ')
961 {
962 at ++;
963 }
964 string logPathString(at);
965 FilePath logPath(logPathString);
966
967 GSInstall install(true);
968 // if we're running in the temporary directory, do the uninstall
969 if (install.setUninstall())
970 {
971 install.uninstall();
972
973 // close the log to write back all changes to the log; if the file
974 // will be deleted, it must be closed; copying may also fail, so it's
975 // safer to close it before doing either.
976 install.recordLog();
977
978 // if the install is empty, terminate all final items (currently the
979 // log and setup executables,
980 if (install.isEmpty())
981 {
982 // delete the log file
983 DeleteFile(logPath.cString());
984
985 // get it's parent to find where the gssetup executable will be
986 FilePath *logParent = logPath.parent();
987 FilePath uninstallPath(*logParent, "gssetup.exe");
988
989 // delete the gssetup executable
990 DeleteFile(uninstallPath.cString());
991
992 MessageBox(0, logParent->cString(), "Removing", MB_OK);
993 if (install.removeFailed(logParent->pathString()))
994 {
995 RemoveDirectory(logParent->cString());
996 }
997
998 // dispose of the parent directory information
999 delete logParent;
1000 }
1001
1002 // if not, then overwrite the original log with the modified one
1003 else
1004 {
1005 // do nothing - should be changed already!
1006 }
1007 }
1008 }
1009 // install wizard
1010 else
1011 {
1012 GSInstall install(false);
1013 GSInstall_init_wizard(install);
1014 if (config_complete == false)
1015 {
1016 MessageBox(0, "Install cancelled", app_name, MB_OK);
1017 }
1018 else
1019 {
1020 // configure the installation
1021 install.setDestination();
1022 install.setManifest();
1023
1024 install.copyFiles(); // caused page fault; Windows 3.1
1025 install.updateProgman(); // caused divide overflow; Windows 3.1
1026 install.updateRegistry();
1027 install.updateProfiles();
1028 install.updateSetupExe(); // caused bomb-out of Windows; 3.1
1029
1030 // close log
1031 install.recordLog();
1032
1033 // do further actions
1034 install.installNetscape();
1035 }
1036 }
1037 DestroyWindow(Window);
1038 }
1039 break;
1040
1041 case WM_USER + 1:
1042 break;
1043
1044 case WM_CLOSE:
1045 /*if (!file_ischanged || IDCANCEL != file_query(Window, file_name))*/
1046 DestroyWindow(Window);
1047 return 0L;
1048
1049 case WM_DESTROY:
1050 {
1051 PostQuitMessage(0);
1052 }
1053 break;
1054
1055 default:
1056 return(DefWindowProc(Window, Message, wParameter, lParameter));
1057 }
1058 return reply;
1059}
1060
1061void GSInstall_init(HINSTANCE instance, int Show)
1062{
1063 int sx, sy;
1064
1065 gsInstall_getDesktopExt(&sx, &sy);
1066
1067 app_window = CreateWindow("GSInstall:Main", "GreenStone Installer",
1068 WS_OVERLAPPEDWINDOW | WS_MAXIMIZE | CS_DBLCLKS,
1069 0, 0,
1070 sx, sy,
1071 NULL,
1072 NULL,
1073 app_instance,
1074 NULL);
1075
1076 ShowWindow(app_window, Show);
1077
1078 PostMessage(app_window, WM_USER, 0, 0L);
1079}
1080
1081void WinClassInit(void)
1082{
1083 WNDCLASS Class;
1084
1085 Class.lpszClassName = "GSInstall:Main";
1086 Class.hInstance = app_instance;
1087 Class.lpfnWndProc = (WNDPROC) GSInstallWindProc;
1088 Class.hCursor = LoadCursor(NULL, IDC_ARROW);
1089 Class.hIcon = NULL; //LoadIcon(app_instance, "GSInstall");
1090 Class.lpszMenuName = NULL;
1091 Class.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE+1);
1092 Class.style = 0;
1093 Class.cbClsExtra = 0;
1094 Class.cbWndExtra = 0;
1095 RegisterClass(&Class);
1096}
1097
1098int PASCAL WinMain(HINSTANCE Current,
1099 HINSTANCE Previous,
1100 LPSTR CmdLine,
1101 int CmdShow)
1102{
1103 MSG msg;
1104
1105 app_cmdLine = (char *) malloc(lstrlen(CmdLine) + 1);
1106 lstrcpy(app_cmdLine, CmdLine);
1107
1108 app_instance = Current;
1109 /* -- init application */
1110 if (!Previous)
1111 {
1112 WinClassInit();
1113 InitCommonControls();
1114 }
1115
1116 /* -- init instance */
1117 GSInstall_init(Current, CmdShow);
1118
1119 while (GetMessage(&msg, NULL, 0, 0))
1120 {
1121 TranslateMessage(&msg);
1122 DispatchMessage(&msg);
1123 }
1124 return (int) msg.wParam;
1125}
Note: See TracBrowser for help on using the repository browser.