source: trunk/gsinstaller/gsinstall.cpp@ 2534

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

Added better monitoring of installation

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