source: other-projects/trunk/gsinstaller/gsinstall.cpp@ 15927

Last change on this file since 15927 was 11703, checked in by mdewsnip, 18 years ago

Added "installation complete" message.

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