// ****************************************************************************
// OWL Extensions (OWLEXT) Class Library
// Copyright (C) 1998 by Dieter Windau
// All rights reserved
//
// ctxhelpm.cpp: implementation file
// Version:      1.2
// Date:         20.10.1998
// Author:       Dieter Windau (used with permission)
//
// TCtxHelpFileManager is a freeware OWL class derived from THelpFileManager
// that add context sensitive help functions similar to the context help
// generated by AppWizard (MFC) or AppExpert (OWL).
//
// TCtxHelpStatusBar is a freeware OWL class that support context sensitive help
//
// You are free to use/modify this code but leave this header intact.
// May not be sold for profit.
//
// Tested with Borland C++ 5.02, OWL 5.02 under Windows NT 4.0 SP3 but I think
// the class would work with Windows 95 too.
// This file is provided "as is" with no expressed or implied warranty.
// Use at your own risk.
//
// Please send me bug reports, bug fixes, enhancements, requests, etc., and
// I'll try to keep this in next versions:
//   EMail: dieter.windau@usa.net
//   Web:   http://www.members.aol.com/softengage/index.htm
// ****************************************************************************
#include <owlext\pch.h>
#pragma hdrstop
 
#include <owlext/ctxhelpm.h>
#include <owl/filename.h>
#include <owl/framewin.h>
#include <owl/dialog.h>
 
#include <owlext/ctxhelpm.rh>
 
using namespace owl;
 
namespace OwlExt {
 
DIAG_DEFINE_GROUP_INIT(OWL_INI, CtxHelp, 1, 0);
 
// private function
TModule* FindResourceModule(TWindow* parent, TModule* module, TResId resId, LPCTSTR type);
 
// ************************* TCtxHelpFileManager ******************************
 
DEFINE_RESPONSE_TABLE1(TCtxHelpFileManager, THelpFileManager)
EV_COMMAND(CM_HELPCONTENTS, CmHelpContents),
EV_COMMAND(CM_HELPCONTEXT, CmHelpContext),
EV_COMMAND_ENABLE(CM_HELPCONTEXT, CeHelpContext),
EV_COMMAND(CM_HELPUSING, CmHelpUsing),
EV_COMMAND(CM_HELPSEARCH, CmHelpSearch),
EV_WM_HELP,
END_RESPONSE_TABLE;
 
TCtxHelpFileManager::TCtxHelpFileManager(const owl::tstring& helpFileName)
:
THelpFileManager(helpFileName)
{
  SupportSystemMenu = false;
  SupportSimpleF1Help = true;
 
  ::LoadString(*FindResourceModule(0,0,IDS_HINTTEXT/16+1,RT_STRING),
    IDS_HINTTEXT, HintText, 128);
 
  // the help cursor is a windows wide default cursor
  //
  hHelpCursor = ::LoadCursor(0, IDC_HELP);
  hOldCursor = 0;
 
  // it is better to hold the full absolute path for the help file
  // because, if the actual path changed, the help file isn't found
  //
  owl::tstring str;
  TCHAR fullPath[_MAX_PATH];
  ::GetModuleFileName(NULL, fullPath, sizeof(fullPath));
  TFileName ExeFileName(fullPath);
  TFileName HelpFileName;
  if(!GetHelpFile().empty())
    HelpFileName = GetHelpFile();
 
  // if there is no device and path, us the applicatin device and path
  //
  if (!(HelpFileName.HasParts(TFileName::Server | TFileName::Device) &&
    HelpFileName.HasParts(TFileName::Path))) {
      if (ExeFileName.HasParts(TFileName::Server | TFileName::Device) &&
        ExeFileName.HasParts(TFileName::Path)) {
          str = ExeFileName.GetParts(
            TFileName::Server | TFileName::Device | TFileName::Path);
      }
  }
  else
    str = HelpFileName.GetParts(
    TFileName::Server | TFileName::Device | TFileName::Path);
 
  // if the is no filename, use the application name
  //
  if (!HelpFileName.HasParts(TFileName::File)) {
    if (ExeFileName.HasParts(TFileName::File))
      str += ExeFileName.GetParts(TFileName::File);
  }
  else
    str += HelpFileName.GetParts(TFileName::File);
 
  // if there is no extension, use the default extension ".hlp"
  //
  if (!HelpFileName.HasParts(TFileName::Ext))
    str += TEXT(".hlp");
  else
    str += HelpFileName.GetParts(TFileName::Ext);
 
  // Set only the the new HelpFile, if the file exist
  //
  TFileName filename(str);
  if (filename.Exists())
    SetHelpFile(str);
  TRACEX(CtxHelp, 0, "TCtxHelpFileManager constructed @" << (void*)this);
}
 
TCtxHelpFileManager::~TCtxHelpFileManager()
{
  TRACEX(CtxHelp, 0, "TCtxHelpFileManager destructed @" << (void*)this);
}
 
void TCtxHelpFileManager::WinHelp(UINT helpID)
// Call WinHelp in mode CONTEXT_HELP with helpID
// If ContextHelp is true, after the call the context help is set to false
{
  TApplication* app = TYPESAFE_DOWNCAST(this, TApplication);
  if (app) {
    app->GetMainWindow()->WinHelp(GetHelpFile().c_str(), HELP_CONTEXT, helpID);
    if (IsContextHelp())
      SetContextHelp(false);
  }
}
 
// Call a yellow popup help window for dialog windows
// and the normal online help for other windows or menus
//
void TCtxHelpFileManager::ActivateHelp(TWindow* wnd, int contextId, uint hlpCmd)
{
  uint cmd = HELP_CONTEXT;
  if (hlpCmd == HELP_INDEX || hlpCmd == HELP_CONTENTS)
    cmd = HELP_FINDER;
#if !defined(BI_COMP_MSC)
  TApplication* app = TYPESAFE_DOWNCAST(this, TApplication);
  if (app) {
    if (wnd) {
      TDialog* dlg = TYPESAFE_DOWNCAST(wnd, TDialog);
      if (dlg) {
        app->GetMainWindow()->WinHelp(GetHelpFile().c_str(),
          HELP_CONTEXTPOPUP, contextId);
        return;
      }
    }
    app->GetMainWindow()->WinHelp(GetHelpFile().c_str(), cmd, contextId);
  }
  else
#endif
    ::WinHelp(0, GetHelpFile().c_str(), cmd, contextId);
}
 
void TCtxHelpFileManager::CmHelpContents()
{
  // Show the help table of contents.
  //
  TApplication* app = TYPESAFE_DOWNCAST(this, TApplication);
  if (app)
    app->GetMainWindow()->WinHelp(GetHelpFile().c_str(), HELP_CONTENTS, 0);
  else
    ::WinHelp(0, GetHelpFile().c_str(), HELP_CONTENTS, 0);
}
 
void TCtxHelpFileManager::CmHelpContext()
{
  // turn context sensitive help on / off
  //
  SetContextHelp((ContextHelp) ? false : true);
}
 
void TCtxHelpFileManager::CeHelpContext(TCommandEnabler& ce)
{
  ce.SetCheck(ContextHelp);
}
 
void TCtxHelpFileManager::CmHelpUsing()
{
  // Display help on help
  //
  TApplication* app = TYPESAFE_DOWNCAST(this, TApplication);
  if (app)
    app->GetMainWindow()->WinHelp(GetHelpFile().c_str(), HELP_HELPONHELP, 0);
  else
    ::WinHelp(0, GetHelpFile().c_str(), HELP_HELPONHELP, 0);
}
 
void TCtxHelpFileManager::CmHelpSearch()
{
  const tchar* EmptyString = _T("");
 
  // Display the seach dialog of help with Empty string
  //
  TApplication* app = TYPESAFE_DOWNCAST(this, TApplication);
  if (app)
    app->GetMainWindow()->WinHelp(GetHelpFile(), HELP_PARTIALKEY,
      reinterpret_cast<ULONG_PTR>(EmptyString));
}
 
bool TCtxHelpFileManager::PreProcessAppMsg(HWND hwnd, uint msg,
                       WPARAM wParam, LPARAM lParam)
                       // Process application messages to provide context sensitive help
{
  MSG aMSG;
  aMSG.hwnd    = hwnd;
  aMSG.message = msg;
  aMSG.wParam  = wParam;
  aMSG.lParam  = lParam;
  return PreProcessAppMsg(aMSG);
}
 
bool TCtxHelpFileManager::PreProcessAppMsg(MSG& msg)
// Process application messages to provide context sensitive help
{
  TApplication* app = TYPESAFE_DOWNCAST(this, TApplication);
  if (!(app))
    return false;
 
  switch (msg.message) {
  case WM_COMMAND:
    if (ContextHelp) {
      WinHelp(static_cast<uint>(msg.wParam));
      return true; // Gobble up the message.
    }
    break;
 
  case WM_SYSCOMMAND: // context sensitive help for system menu command
    if (ContextHelp && SupportSystemMenu) {
      if (msg.wParam == SC_SIZE ||
        msg.wParam == SC_MOVE ||
        msg.wParam == SC_MINIMIZE ||
        msg.wParam == SC_MAXIMIZE ||
        msg.wParam == SC_NEXTWINDOW ||
        msg.wParam == SC_PREVWINDOW ||
        msg.wParam == SC_CLOSE ||
        msg.wParam == SC_RESTORE ||
        msg.wParam == SC_TASKLIST ||
        msg.wParam == SC_HSCROLL ||
        msg.wParam == SC_VSCROLL) {
 
          WinHelp(static_cast<uint>(msg.wParam));
          return true; // Gobble up the message.
      }
    }
    break;
 
  case WM_NCLBUTTONDOWN: // context sensitive help for application decorations
    if (ContextHelp && SupportSystemMenu) {
      DWORD dwHelpContextId = 0;
      switch (msg.wParam) {
  case HTZOOM:        dwHelpContextId = SC_MAXIMIZE; break;
  case HTREDUCE:      dwHelpContextId = SC_MINIMIZE; break;
  case HTCAPTION:     dwHelpContextId = HID_TITLEBAR; break;
  case HTVSCROLL:     dwHelpContextId = SC_VSCROLL; break;
  case HTHSCROLL:     dwHelpContextId = SC_HSCROLL; break;
 
  case HTSYSMENU:     // allow the menus to popup
  case HTMENU:        break;
 
  case HTBOTTOM:
  case HTBOTTOMLEFT:
  case HTBOTTOMRIGHT:
  case HTTOP:
  case HTLEFT:
  case HTRIGHT:
  case HTTOPLEFT:
  case HTTOPRIGHT:    dwHelpContextId = SC_SIZE; break;
      }
 
      if (dwHelpContextId != 0) {
        WinHelp(dwHelpContextId);
        return true; // Gobble up the message.
      }
    }
    break;
 
  case WM_KEYDOWN:
    if (ContextHelp && msg.wParam == VK_ESCAPE) {
      // turn the context sensitive help off
      //
      SetContextHelp(false);
      return true; // Gobble up the message.
    }
 
    // BEGIN HARDCODE ACCELERATORS
    //
    // If you have other accelerators in your app delete this section
    //
    if (msg.wParam == VK_F1) {
      if (::GetKeyState(VK_SHIFT) < 0) {
        // If Shift+F1 call the CmHelpContext() function
        //
        CmHelpContext();
        return true; // Gobble up the message.
      }
      else if (::GetKeyState(VK_CONTROL) < 0) {
        // If Ctrl+F1 call the CmHelpSearch() function
        //
        if (!ContextHelp)
          CmHelpSearch();
        return true; // Gobble up the message.
      }
      else {
        // If only F1 call the CmHelpContents() function
        //
        SetContextHelp(false);
        CmHelpContents();
        return true; // Gobble up the message.
      }
    }
    //
    // END HARDCODE ACCELERATORS
    break;
 
 
    //  case WM_LBUTTONDOWN:   // catch this messages in all windows
    //  case WM_NCLBUTTONDOWN: // that has a context sensitive help
    // e.g. statusbar, client window
 
  case WM_NCMOUSEMOVE:
  case WM_MOUSEMOVE:
  case WM_LBUTTONUP:
  case WM_LBUTTONDBLCLK:
  case WM_NCRBUTTONDOWN:
  case WM_RBUTTONDOWN:
  case WM_RBUTTONUP:
  case WM_RBUTTONDBLCLK:
  case WM_NCMBUTTONDOWN:
  case WM_MBUTTONDOWN:
  case WM_MBUTTONUP:
  case WM_MBUTTONDBLCLK:
  case WM_INITMENU:
    if (ContextHelp) {
      if (::GetCursor() != hHelpCursor)
        ::SetCursor(hHelpCursor);
      return true; // Gobble up the message.
    }
    break;
 
  case WM_MENUSELECT:
    if ((HIWORD(msg.wParam) & MF_POPUP) && msg.lParam) {
      // Use TMenu, because the GetMenuItemID function returns
      // a value-1 if the menu is a popup menu
      //
      TMenu menu((HMENU)msg.lParam);
      MenuItemId = menu.GetMenuItemID(LOWORD(msg.wParam));
    }
    break;
 
  default:
    break;
 
  } // end of switch
  return false;
}
 
void TCtxHelpFileManager::SetContextHelp(bool contextHelp)
{
  if (ContextHelp != contextHelp) {
    ContextHelp = contextHelp;
 
    TApplication* app = TYPESAFE_DOWNCAST(this, TApplication);
    if (!(app))
      return;
 
    TCtxHelpStatusBar* statusBar = TYPESAFE_DOWNCAST(
      app->GetMainWindow()->ChildWithId(IDW_STATUSBAR), TCtxHelpStatusBar);
 
    if (ContextHelp) {
      hOldCursor = ::SetCursor(hHelpCursor);
      if (statusBar) {
        statusBar->SetHintText(HintText);
        statusBar->Block();
      }
    }
    else {
      ::SetCursor(hOldCursor);
      hOldCursor = 0;
      if (statusBar) {
        statusBar->UnBlock();
        statusBar->ClearHintText(); // Restore text of status bar
      }
    }
  }
}
 
void TCtxHelpFileManager::EvHelp(const HELPINFO& hi)
// Copy more or less from THelpFileManager. Add simple help support for menus
{
  THelpContext context;
  bool success = false;
 
  if (hi.iContextType == HELPINFO_MENUITEM)
    success = GetHelpContextFromMenu(context, hi.iCtrlId);
  else if (hi.iContextType == HELPINFO_WINDOW)
    success = GetHelpContextFromControl(context, hi.iCtrlId,
    (HWND)hi.hItemHandle);
 
  if (success)
    ActivateHelp(context.GetWindow(), context.GetHelpFileContextId());
  else {
 
    // if there isn't a entry in the table
    //
    if (SupportSimpleF1Help && hi.iContextType == HELPINFO_MENUITEM) {
 
      // support simple F1 help (menu commandid = helpid)
      //
      TApplication* app = TYPESAFE_DOWNCAST(this, TApplication);
      if (app) {
 
        // show only help for system menu commands, if they are supported
        //
        if ((SupportSystemMenu == false) && (
          hi.iCtrlId == SC_SIZE ||
          hi.iCtrlId == SC_MOVE ||
          hi.iCtrlId == SC_MINIMIZE ||
          hi.iCtrlId == SC_MAXIMIZE ||
          hi.iCtrlId == SC_NEXTWINDOW ||
          hi.iCtrlId == SC_PREVWINDOW ||
          hi.iCtrlId == SC_CLOSE ||
          hi.iCtrlId == SC_RESTORE ||
          hi.iCtrlId == SC_TASKLIST))
          return;
 
        if (hi.iCtrlId == 0) {
 
          // Activate help with the id, that is copied in WM_MENUSELECT
          //
          ActivateHelp(app->GetMainWindow(), MenuItemId);
        }
        else
          ActivateHelp(app->GetMainWindow(), hi.iCtrlId);
      }
    }
  }
}
 
// ************************* TCtxHelpStatusBar ********************************
 
DEFINE_RESPONSE_TABLE1(TCtxHelpStatusBar, TStatusBar)
EV_WM_LBUTTONDOWN,
END_RESPONSE_TABLE;
 
TCtxHelpStatusBar::TCtxHelpStatusBar(TWindow* parent,
                   TGadget::TBorderStyle borderStyle,
                   uint modeIndicators,
                   TFont* font,
                   TModule* module):
TStatusBar(parent, borderStyle, modeIndicators, font, module)
{
  block = false;
  TRACEX(CtxHelp, 0, "TCtxHelpStatusBar constructed @" << (void*)this);
}
 
TCtxHelpStatusBar::~TCtxHelpStatusBar()
{
  TRACEX(CtxHelp, 0, "TCtxHelpStatusBar destructed @" << (void*)this);
}
 
void TCtxHelpStatusBar::SetHintText(const owl::tstring& text)
// Show only the hint text, if the output is not blocked
{
  if (block == false)
    TStatusBar::SetHintText(text);
}
 
void TCtxHelpStatusBar::SetHintText(int i)
{
  PRECONDITION(i == 0); InUse(i);
  ClearHintText();
}
 
void TCtxHelpStatusBar::EvLButtonDown(uint modKeys, const TPoint& point)
{
  TCtxHelpFileManager* ctxHelpM = TYPESAFE_DOWNCAST(GetApplication(),
    TCtxHelpFileManager);
 
  // Show help only, if the ContextHelp is true and the help for system menu
  // and application decorations is supported
  //
  if (ctxHelpM && ctxHelpM->IsContextHelp() && ctxHelpM->IsSupportSystemMenu()) {
    ctxHelpM->WinHelp(IDW_STATUSBAR);
  }
  else
    TStatusBar::EvLButtonDown(modKeys, point);
}
 
} // OwlExt namespace
 
//==============================================================================

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: MenuItemId.

V530 The return value of function 'LoadStringA' is required to be utilized.

V688 The 'HelpFileName' local variable possesses the same name as one of the class members, which can result in a confusion.