view anagram/vaclgui/vaclgui.cpp @ 14:a02e9434072e

Fix friend declaration for gcc10. XXX: did not check it against the IBM compiler, might end up needing XXX: to be conditional.
author David A. Holland
date Tue, 31 May 2022 00:59:42 -0400
parents 13d2b8934445
children
line wrap: on
line source

/*
 * AnaGram, A System for Syntax Directed Programming
 * Copyright 1997-2002 Parsifal Software. All Rights Reserved.
 * See the file COPYING for license and usage terms.
 *
 * vaclgui.cpp
 */

//#include <iaccel.hpp>
#include <iapp.hpp>
#include <icoordsy.hpp>
#include <iexcept.hpp>
#include <ifont.hpp>
//#include <igroupbx.hpp>
#include <iiconctl.hpp>
//#include <iinfoa.hpp>
#include <imsgbox.hpp>
#include <ipainevt.hpp>
#include <ipainhdr.hpp>
//#include <iprofile.hpp>
#include <irefcnt.hpp>

#include "action.h"
#include "agcstack.h"
#include "aglib.h"
#include "agrect.hpp"
#include "agstring.h"
#include "assert.h"
#include "engdef.h"
#include "ctrlpanel.hpp"
#include "base.h"
#include "bpu.h"
#include "help.h"
#include "multiline.hpp"
#include "operations.h"
#include "textfile.h"
#include "sums-defs.h"
#include "vaclgui-res.h"
#include "vaclgui.hpp"
#include "version.h"

//#define INCLUDE_LOGGING
#include "log.h"


#define DLL


ActionWindow *pActionWindow = 0;

IPoint         cascadeIncrement;
IPoint         cascadeOffset(0,0);
IPoint         cascadeOrigin(0,0);
AgString       commandLineFile;
IPointerHandle iconHandle;
AgAction       idAction;
text_file      inputFile;
IFont          menuFont;
int            messageBoxShowing = 0;


// declared in base.h
int nextChildId(void) {
  //LOGSECTION("nextChildId");
  static int next = 0x1000;
  //LOGV(next);
  return next++;
}

void messageBeep() {
  LOGSECTION("messageBeep");
  MessageBeep(MB_ICONEXCLAMATION);
}

AgQuadrant findQuadrant(IWindow *w) {
  RECT r;
  SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
  AgRectangle desktop(r);
  cint sz = desktop.size()/2;
  AgQuadrant quad = upperLeft;
  AgRectangle wRect(w);
  int i;
  int area[4] = {0, 0, 0, 0};
  for (i = 0; i < 4; i++) {
    AgQuadrant corner = (AgQuadrant) i;
    AgQuadrant quadrant = (AgQuadrant) (i^3);
    AgRectangle test(desktop.position(corner), sz, quadrant);
    area[i] = test.intersection(wRect).area();
    if (area[i] > area[quad]) {
      quad = (AgQuadrant) i;
    }
  }
  return quad;
}

static const struct {
  AgQuadrant corner, quadrant;
} placement[8] = {
  { lowerLeft,  lowerRight },
  { lowerRight, lowerLeft },
  { upperLeft,  upperRight },
  { upperRight, upperLeft },
  { upperRight, lowerRight },
  { lowerRight, upperRight },
  { upperLeft,  lowerLeft },
  { lowerLeft,  upperLeft }
};

static cint positionWindow(cint measure, LayoutRef reference, cint sz) {
  LOGSECTION("positionWindow");
  int i;
  int area[8];
  AgRectangle test[8];
  for (i = 0; i < 8; i++) {
    AgQuadrant corner   = placement[i].corner;
    AgQuadrant quadrant = placement[i].quadrant;
    LOGV(i) LCV(corner) LCV(quadrant);

    test[i] = AgRectangle(reference.position(corner), sz, quadrant);
    LOGV(reference.position(corner));
    LOGV(test[i].position());
    test[i].limit(measure);
    LOGV(test[i].position());
    area[i] = reference.overlap(test[i]);
    LOGV(test[i].position()) LCV(test[i].size()) LCV(area[i]);
  }
  int minArea = area[0];
  int optimalPlacement = 0;
  for (i = 1; i < 8; i++) {
    if (area[i] >= minArea) {
      continue;
    }
    minArea = area[i];
    optimalPlacement = i;
  }
  LOGV(minArea) LCV(optimalPlacement);
  return test[optimalPlacement].position(upperLeft);
}

IRectangle adjustPos(IRectangle &r) {
  RECT rd;
  SystemParametersInfo(SPI_GETWORKAREA, 0, &rd, 0);
  IRectangle desktop(rd.left, rd.top, rd.right, rd.bottom);
  ISize newSize = r.size().minimum(desktop.size());
  newSize = newSize.maximum(ISize(100, 100));
  r.sizeTo(newSize);
  IPoint where = r.maxXMaxY().minimum(desktop.maxXMaxY());
  where -= newSize;
  r.moveTo(where);
  where = r.minXMinY().maximum(IPoint(0, 0));
  r.moveTo(where);
  return r;
}

class Damaged : public AgFrame {
private:
  MultiLineText message;

public:
  Damaged(const char *failure);
  ~Damaged();
  void showme();
};

Damaged::Damaged(const char *failure)
  : AgFrame(IFrameWindow::border
            | dialogBackground
            | IFrameWindow::windowList
            | IFrameWindow::systemMenu)
  , message(this)
{
  LOGSECTION("Damaged");
  AgString f1 = "Initialization failure: ";
  AgString f2 = failure;
  AgString f3 = code_segment("broken");
  AgString msg = f1.concat(f2).concat("\n\v").concat(f3);
  LOGV(msg);
  message.setText(msg.pointer());
  IFont textFont = IFont("Arial", 9);
  message.setFont(textFont);
  int leading = textFont.maxSize().height() + textFont.externalLeading();
  LOGV(leading);
  message.setVTab(leading/2);
}

Damaged::~Damaged() {
  pActionWindow->close();
}

void Damaged::showme() {
  LOGSECTION("Damaged::show");
  ISize frameSize = frameRectFor(IRectangle(message.minimumSize())).size();
  LOGV(frameSize.asString());
  setClient(&message);
  sizeTo(frameSize);
  IPoint where = (IPair) place(IWindow::desktopWindow()->size(),
			       frameSize, 11);
  LOGV(where.asString());
  moveTo(where);
  show().setFocus();
}

class SplashTimer : public ITimerFn {
private:
  IFrameWindow *window;
  ITimer &timer;
  AgAction finish;

public:
  SplashTimer(IFrameWindow *, ITimer &);
  SplashTimer(IFrameWindow *, ITimer &, AgAction);
  void timerExpired(unsigned long timerId);
};

SplashTimer::SplashTimer(IFrameWindow *window_,
			 ITimer &timer_)
  : window(window_)
  , timer(timer_)
{}

SplashTimer::SplashTimer(IFrameWindow *window_,
			 ITimer &timer_,
			 AgAction f)
  : window(window_)
  , timer(timer_)
  , finish(f)
{}

void SplashTimer::timerExpired(unsigned long) {
  LOGSECTION("SplashTimer::timerExpired");
  timer.stop();
  window->close();
  LOGS("splash window closed");
  finish.performDeferred();
}

class ReleaseSplash
  : public IFrameWindow
  , public IPaintHandler
{
private:
  ISetCanvas canvas;
    ISetCanvas top;
      IIconControl myicon;
      IStaticText logo;
      ICanvas   balance;
    IStaticText progName;
    MultiLineText copyright;
    //IStaticText licenseBox;
    //MultiLineText licensee;
  static ITimer timer;

public:
  ReleaseSplash();
  ~ReleaseSplash();
  Boolean paintWindow(IPaintEvent &event);
  static void makeReleaseSplash();
};

ReleaseSplash::~ReleaseSplash() {
  IPaintHandler::stopHandlingEventsFor(&canvas);
  IPaintHandler::stopHandlingEventsFor(&top);
  IPaintHandler::stopHandlingEventsFor(&balance);
}

Boolean ReleaseSplash::paintWindow(IPaintEvent &event) {
  LOGSECTION("ReleaseSplash::paintWindow");
  LOGV(event.rect().asString());
  event.clearBackground(IGUIColor::dialogBgnd);
  return false;
}

ITimer ReleaseSplash::timer;

ReleaseSplash::ReleaseSplash()
  : IFrameWindow(nextChildId(),
                 AgFrame::frameParent,
                 AgFrame::frameOwner,
                 IRectangle(),
                 dialogBackground | dialogBorder)
  , canvas(nextChildId(),this, this, IRectangle(),
	     ISetCanvas::verticalDecks
	   | ISetCanvas::packTight
	   | ISetCanvas::centerAlign
	   | IWindow::clipChildren
	   | IWindow::visible)
  , top(nextChildId(), &canvas, &canvas, IRectangle(),
	  ISetCanvas::horizontalDecks
	| ISetCanvas::packTight
	| ISetCanvas::centerVerticalAlign
	| IWindow::clipChildren
	| IWindow::visible
      )
  , myicon(nextChildId(), &top, &top, iconHandle,IRectangle(),
	   IIconControl::classDefaultStyle | IIconControl::fillBackground)
  , logo(nextChildId(), &top, &top)
  , balance(nextChildId(), &top, &top)
  , progName(nextChildId(), &canvas, &canvas)
  , copyright(&canvas)
  //, licenseBox(nextChildId(), &canvas, &canvas)
  //, licensee(&canvas, IRectangle(),
  //             IStaticText::classDefaultStyle
  //           | IStaticText::border3D)
{
  IPaintHandler::handleEventsFor(&canvas);
  IPaintHandler::handleEventsFor(&top);
  IPaintHandler::handleEventsFor(&balance);
  //myicon.setBackgroundColor(IGUIColor::dialogBgnd);
  logo.setFont(IFont("Arial", 20));
  logo.font().setBold();
  logo.setText("AnaGram");
  IFont textFont("Arial",8);
  progName.setFont(IFont("Arial",12));
  progName.font().setBold();
  progName.setText(BASEVERSIONSTRING);
  copyright.setFont(IFont("Arial", 8));
  //
  // This used to have a literal iso-latin-1 copyright char in it.
  // However, I've been compelled to remove it, including even from
  // this comment, not because any compiler rejects it (though they
  // would technically be within their rights) but because Red Hat's
  // foolish and reckless default locale settings and/or Red Hat's
  // custom patch set seem to render Red Hat-based versions of Emacs
  // incapable of touching the file without mangling it.
  //
  // I am not terribly surprised to discover this. RH seems to do a
  // lot of work to make sure they can put the 'sux' in 'linsux'.
  //
  // (i18n is a fine thing, but it has to *work*, especially since
  // non-English speakers can't just shut it all off and do without.)
  //
  // Fortunately, the copyright character is not legally necessary.
  //
  copyright.setText("Copyright 1993-2002 Parsifal Software. "
		    "All Rights Reserved.\n"
		    "Copyright 2006, 2007 David A. Holland. "
		    "All Rights Reserved.\n"
		    "\n"
		    "This version of AnaGram is free software.\n"
		    "There is NO WARRANTY.\n"
		    "See \"license\" in the help system for details.\n");
  copyright.setMinimumSize(copyright.calcMinimumSize());

  //licenseBox.setFont(textFont);
  //licenseBox.setText("This copy of AnaGram is registered to:");

  //licensee.setFont(textFont);
  //licensee.setText(licenseeText().pointer());
  //LOGV(licenseBox.font().name()) LCV(licenseBox.font().pointSize());
  //LOGV(licensee.font().name()) LCV(licensee.font().pointSize());
  //int leading = textFont.maxSize().height() + textFont.externalLeading();
  //licensee.setMargin(leading, leading);
  //licensee.setVTab(leading/2);

  canvas.setDeckCount(1);
  top.setDeckCount(1);
  setClient(&canvas);
  //myicon.sizeTo(myicon.minimumSize());
  //balance.sizeTo(myicon.minimumSize());
  balance.setMinimumSize(myicon.minimumSize());
  //logo.sizeTo(logo.minimumSize());
  //top.sizeTo(top.minimumSize());
  //licenseBox.sizeTo(licenseBox.minimumSize());

  //ISize minSize = licenseBox.minimumSize();
  //ISize licenseeSize = licensee.calcMinimumSize();
  //if (licenseeSize.width() < minSize.width()) {
  //  licenseeSize.setWidth(minSize.width());
  //}
  //licensee.setMinimumSize(licenseeSize);

  ISize size = canvas.minimumSize();
  canvas.sizeTo(size);
  IRectangle r = frameRectFor(IRectangle(size));
  ISize windowSize = r.size();
  sizeTo(windowSize);
  ISize desktopSize = IWindow::desktopWindow()->size();
  ISize margin = (desktopSize - windowSize)/2;
  moveTo((IPoint) margin);
}

void ReleaseSplash::makeReleaseSplash() {
  LOGSECTION("Splash::makeReleaseSplash");
  ReleaseSplash *window = new ReleaseSplash;
  window->setAutoDeleteObject();
  window->show().refresh(IWindow::immediate);
#ifdef INCLUDE_LOGGING
  int posFlag =
#endif
    SetWindowPos(window->handle(),
		 HWND_TOPMOST,
		 0,0,0,0,
		 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
#ifdef INCLUDE_LOGGING
  if (posFlag == 0) {
    LOGV(GetLastError());
  }
#endif

  IReference<ITimerFn> timerFn(new SplashTimer(window, timer));
  window->timer.start(timerFn, 4000);
}

void bailOut(const char *problem) {
  LOGSECTION("bailOut");
  LOGV(problem);
  LOGSTACK;
  if (controlPanel->timer.isStarted()) {
    controlPanel->timer.stop();
  }
  IMessageBox messageBox(0);
  messageBox.setTitle("AnaGram");
  char msg[200];
  sprintf(msg, "%s\nUnexpected program termination", problem);
  messageBoxShowing++;
  messageBox.show(msg, IMessageBox::catastrophic);
  messageBoxShowing--;
  abort();
}

//void abortProgram() {
//  pActionWindow->close();
//}

static void setup() {
  LOGSECTION("setup");

  init_parser();

  const char *failure = checksums_ok();
  LOGS("checksums_ok call returned");
  LOGV(failure);

  if (failure) {
    Damaged *failwindow = new Damaged(failure);
    failwindow->setAutoDeleteObject();

    idAction = actionObject(failwindow, Damaged::showme);
  }
  else {
    idAction = actionObject(ControlPanel::makeControlPanel);
  }

  idAction.performDeferred();
}

void agGui(char *f) {
  LOGSECTION("agGui");
  LOGV((void *) agGui);

  AgString file(f);
  try {
    commandLineFile = file;
    LOGV(commandLineFile);
    ICoordinateSystem::setApplicationOrientation(
      ICoordinateSystem::originUpperLeft);

    iconHandle = IResourceLibrary().loadIcon(IDI_ICON, 0);
    ActionWindow actionWindow;
    pActionWindow = &actionWindow;
    AgAction::startup();
    AgFrame::frameOwner = pActionWindow;
    AgFrame::frameParent = IWindow::desktopWindow();
    engdef_init();
    help_init();
    defer(ReleaseSplash::makeReleaseSplash);
    defer(setup);

    LOGS("Starting program");
    IApplication::current().run();
    LOGS("Program complete");
  }
  catch(IException & IFLOG(ie)) {
    AgFrame::windowRegistry.clearActive();
    LOGSECTION_ON("catch(IException)");
    LOGSTACK;
    LOGEXCEPTION(ie);
    LOGS("Returned from logging exception");
    assert(0);
/*
    IMessageBox messageBox(0);
    LOGS("Created message box");
    messageBox.setTitle("AnaGram");
    LOGS("title set");
    messageBox.show(
      "AnaGram: Unexpected program termination",
      IMessageBox::catastrophic);
    LOGS("Returned from showing message");
*/
  }
  catch(Problem p) {
    AgFrame::windowRegistry.clearActive();
    LOGSECTION("catch(Problem)");
    LOGV(p.msg);
    LOGSTACK;
    assert(0);
/*
    IMessageBox messageBox(0);
    messageBox.setTitle("AnaGram");
    char msg[200];
    sprintf(msg, "Unexpected program termination\n%s", p.msg);
    messageBoxShowing++;
    messageBox.show(msg, IMessageBox::catastrophic);
    messageBoxShowing--;
*/
  }
  catch(...) {
    AgFrame::windowRegistry.clearActive();
    LOGSECTION_ON("catch(...)");
    LOGSTACK;
    assert(0);
/*
    IMessageBox messageBox(0);
    messageBox.setTitle("AnaGram...");
    messageBoxShowing++;
    messageBox.show(
      "AnaGram: Unexpected program termination",
      IMessageBox::catastrophic);
    messageBoxShowing--;
*/
  }
}

#if 0 /* was: ifndef DLL */

/**************************************************************/
/* historic main() program entry point                        */
/**************************************************************/
int main(int argc, char *argv[]) {
  init(argv[0]);

  int buildSwitch = 0;
  int i;
  AgString file;
  for (i = 1; i < argc; i++) {
    switch (argv[i][0]) {
      case '/':
      case '-':
        switch (argv[i][1]) {
          case 'b':
          case 'B':
            buildSwitch = 1;
            LOGS("set buildSwitch");
            break;
        }
        break;
      default:
	file = argv[i];
	break;
    }
  }
  if (buildSwitch) {
    return commandLineBuild(file.pointer());
  }
  agGui(file.pointer());
  return 0;
}

#endif /* 0 */

IPoint placeOnDesktop(ISize s, int ij) {
  RECT r;
  SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
  ISize desktopSize(r.right - r.left, r.bottom - r.top);
  return (IPair) place(desktopSize, s, ij);
}

IPoint placeRelative(ISize s, IWindow *window) {
  RECT r;
  SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
  ISize desktopSize(r.right - r.left, r.bottom - r.top);

  AgRectangle ref(window->position(), window->size());
  return (IPair) positionWindow(desktopSize, ref, s);
}

IPoint placeWindow(ISize s, LayoutRef ref) {
  RECT r;
  SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
  ISize desktopSize(r.right - r.left, r.bottom - r.top);
  return (IPair) positionWindow(desktopSize, ref, s);
}