//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1993, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of class TListBoxView
//----------------------------------------------------------------------------
#include <owl/pch.h>
 
#include <owl/listboxview.h>
#include <owl/inputdia.h>
#include <owl/listboxview.rh>
#include <owl/docview.rh>
#include <owl/edit.rh>
#include <stdio.h>
 
using namespace std;
 
namespace owl {
 
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlDocView);        // General Doc/View diagnostic group
 
// Let the compiler know that the following template instances will be defined elsewhere.
//#pragma option -Jgx
 
 
const tchar VirtualLastLineStr[] = _T("---");  // Last virtual line appended to list
 
DEFINE_RESPONSE_TABLE1(TListBoxView, TListBox)
  EV_COMMAND(CM_EDITUNDO,   CmEditUndo),
  EV_COMMAND(CM_EDITCUT,    CmEditCut),
  EV_COMMAND(CM_EDITCOPY,   CmEditCopy),
  EV_COMMAND(CM_EDITPASTE,  CmEditPaste),
  EV_COMMAND(CM_EDITCLEAR,  CmEditClear),
  EV_COMMAND(CM_EDITDELETE, CmEditDelete),
  EV_COMMAND(CM_EDITADD,    CmEditAdd),
  EV_COMMAND(CM_EDITEDIT,   CmEditItem),
  EV_WM_GETDLGCODE,
  EV_NOTIFY_AT_CHILD(LBN_DBLCLK, CmEditItem),
  EV_NOTIFY_AT_CHILD(LBN_SELCHANGE, CmSelChange),
  EV_VN_DOCCLOSED,
  EV_VN_ISWINDOW,
  EV_VN_ISDIRTY,
  EV_VN_COMMIT,
  EV_VN_REVERT,
END_RESPONSE_TABLE;
 
//
/// Creates a TListBoxView object associated with the specified document and parent
/// window. Sets Attr.AccelTable to IDA_LISTVIEW to identify the edit view. Sets the
/// view style to WS_HSCROLL | LBS_NOINTEGRALHEIGHT.
/// Sets TView::ViewMenu to the new TMenuDescr for this view.
//
TListBoxView::TListBoxView(TDocument& doc, TWindow* parent)
:
  TListBox(parent, GetNextViewId(), 0,0,0,0),
  TView(doc),
  DirtyFlag(false),
  Origin(0),
  MaxWidth(0)
{
  Attr.Style &= ~(LBS_SORT);
  Attr.Style |= (WS_HSCROLL | LBS_NOINTEGRALHEIGHT);
  Attr.AccelTable = IDA_LISTVIEW;
  if (::FindResource(*GetModule(), TResId(IDM_LISTVIEW), RT_MENU))
    SetViewMenu(new TMenuDescr(IDM_LISTVIEW, 0,1,0,0,0,1, GetModule()));
}
 
//
/// Adds a string into the view. Returns the index at which the string is added.
//
int
TListBoxView::AddString(LPCTSTR str)
{
  long style = GetWindowLong(GWL_STYLE);
  if (!(style & LBS_SORT)) {
    int itemsInListBox = GetCount();
    if (itemsInListBox > 0) {
      // before the end of list marker
      return InsertString(str, itemsInListBox-1);
    }
  }
  return TListBox::AddString(str);
}
 
//
/// Sets the maximum horizontal extent for the list view window.
//
void
TListBoxView::SetExtent(const tstring& str)
{
  if (str.length() == 0)
    return;
 
  TClientDC  clientDC(*this);
  const auto extent = clientDC.GetTextExtentPoint32(str) + TSize{2, 0}; // Room for focus rectangle.
  if (extent.cx > MaxWidth)
    SetHorizontalExtent(MaxWidth = extent.cx);
}
 
//
/// Indicates that the document has been closed.
//
bool
TListBoxView::VnDocClosed(int omode)
{
  if (DirtyFlag == 2 || !(omode & ofWrite))  // make sure someone else's write
    return false;
  int top = GetTopIndex();
  int sel = GetSelIndex();
  LoadData(top, sel);
  return true;
}
 
//
/// Reads the view from the stream and closes the file. Returns true if the view was
/// successfully loaded.
/// Throws an xmsg exception and displays the error message TListBoxView initial read
/// error if the file can't be read. Returns false if the view can't be loaded.
//
bool
TListBoxView::LoadData(int top, int sel)
{
  CmEditClear();    // Clear list & remove virtual last line temporarily
  DeleteString(0);
 
  long style = GetWindowLong(GWL_STYLE);
  if (!(style & LBS_SORT))
    TListBox::AddString(VirtualLastLineStr);     // Append virtual last line
 
  DirtyFlag = false;
 
  tistream* inStream;
  if ((inStream = Doc->InStream(ios::in)) == 0) {
    Doc->PostError(IDS_UNABLEOPEN, MB_OK);
    return false;
  }
  bool status;
  for (;;) {
    tchar buf[100+1];
    inStream->getline(buf, COUNTOF(buf)-1);
    if (!inStream->gcount() && !inStream->good()) {
      status = ToBool(inStream->eof());
      break;
    }
    AddString(buf);
    SetExtent(buf);
  }
  SetTopIndex(top);
  SetSelIndex(sel);
  delete inStream;   // close file in case process switch
  if (!status)
    Doc->PostError(IDS_READERROR, MB_OK);
  return status;
}
 
//
/// Overrides TWindow::Create and creates the view's window. Determines if the file
/// is new or already has data. If there is data, calls LoadData to add the data to
/// the view. If the view's window can't be created, Create throws a TXWindow
/// exception.
//
bool
TListBoxView::Create()
{
  try {
    TListBox::Create();   // throws exception TWindow::TXWindow
  }
  catch (TXOwl& ) {
    Doc->PostError(IDS_NOMEMORYFORVIEW, MB_OK);
    return true;   // cannot return false - throws another exception
  }
  if (Doc->GetDocPath() == 0) {
    CmEditClear();         // perform any clearing initialization
    return true;           // new file, no data to display
  }
  if (!LoadData(0, 0))
    NotOK();
  return true;
}
 
//
/// Commits changes made in the view to the document. If force is nonzero, all data,
/// even if it's unchanged, is saved to the document.
//
bool
TListBoxView::VnCommit(bool force)
{
  if (!force && !DirtyFlag)
    return true;
 
  tostream* outStream = Doc->OutStream(ios::out);
  if (outStream == 0) {
    Doc->PostError(IDS_UNABLEOPEN, MB_OK);
    return false;
  }
  outStream->seekp(Origin);
  int count = GetCount();
  for (int index = 0; index < count-1; index++) {  // don't write last virtual line
    int len = GetStringLen(index);
    TAPointer<tchar> buf(new tchar[len+1]);
    GetString((tchar*)buf, index);
    *outStream << (tchar*)buf << _T('\n');
    //delete buf;
  }
  DirtyFlag = 2;           // to detect our own close notification
 
  bool status = ToBool(outStream->good());
  delete outStream;
  DirtyFlag = false;
  if (!status)
    Doc->PostError(IDS_WRITEERROR, MB_OK);
 
  return status;
}
 
//
/// Indicates if changes made to the view should be erased, and if the data from the
/// document should be restored to the view. If clear is a nonzero value, the data
/// is cleared instead of restored to the view.
//
bool
TListBoxView::VnRevert(bool clear)
{
  if (!clear && Doc->GetDocPath() != 0)
    return LoadData(0,0);
  CmEditClear();
  DirtyFlag = false;
  return true;
}
 
//
/// Overrides TWindow's response to a WM_GETDLGCODE message (an input procedure
/// associated with a control that isn't a check box) by calling
/// TWindow::DefaultProcessing.
/// EvGetDlgCode returns a code that indicates how the list box control message is
/// to be treated.
//
uint
TListBoxView::EvGetDlgCode(const MSG*)
{
  uint retVal = (uint)DefaultProcessing();
  retVal |= DLGC_WANTCHARS;
  return retVal;
}
 
//
/// Handler to undo the last operation performed on the underlying ListBox.
/// \note This feature is not implemented in the current version of ObjectWindows.
//
void
TListBoxView::CmEditUndo()
{
#if BI_MSG_LANGUAGE == 0x0411
  MessageBox("���̋@�\�͎�������Ă��܂���", "���ɖ߂�", MB_OK);
#else
  MessageBox(_T("Feature not implemented"), _T("Undo"), MB_OK);
#endif
}
 
//
/// Automatically responds to a menu selection with a menu ID of CM_EDITCUT by
/// calling CmEditCopy and CmEditDelete to delete a text string from the list view.
/// Sets the data member DirtyFlag to true.
//
void
TListBoxView::CmEditCut()
{
  CmEditCopy();
  CmEditDelete();
}
 
//
/// Automatically responds to a menu selection with a menu ID of CM_EDITCOPY and
/// copies the selected text to the Clipboard.
//
void
TListBoxView::CmEditCopy()
{
  int index = GetSelIndex();
  int count = GetCount();
  if (count <= 1 || index >= count)
    return;
 
  TClipboard cb(*this, false);
  if (cb.EmptyClipboard()) {
    int len = GetStringLen(index);
    HANDLE cbhdl = ::GlobalAlloc(GHND,len+0+1);
    LPTSTR buf = (LPTSTR)::GlobalLock(cbhdl);
    GetString(buf, index);
    ::GlobalUnlock(cbhdl);
#if defined(UNICODE)
    cb.SetClipboardData(CF_UNICODETEXT, cbhdl);
#else
    cb.SetClipboardData(CF_TEXT, cbhdl);
#endif
  }
}
 
//
/// Automatically responds to a menu selection with a menu ID of CM_EDITPASTE by
/// inserting text into the list box using functions in TListBox.
//
void
TListBoxView::CmEditPaste()
{
  int index = GetSelIndex();
  if (index < 0)
    index = 0;
 
  TClipboard cb(*this, false);
  if (!cb)
    return;   // clipboard open by another program
 
#if defined(UNICODE)
  HANDLE cbhdl = cb.GetClipboardData(CF_UNICODETEXT);
#else
  HANDLE cbhdl = cb.GetClipboardData(CF_TEXT);
#endif
  if (cbhdl) {
    LPTSTR text = (LPTSTR)::GlobalLock(cbhdl);
    InsertString(text, index);
    SetSelIndex(index+1);
    DirtyFlag = true;
    ::GlobalUnlock(cbhdl);
  }
}
 
//
/// Automatically responds to a menu selection with a menu ID of CM_EDITDELETE by
/// deleting the currently selected text.
//
void
TListBoxView::CmEditDelete()
{
  int count = GetCount();
  int index = GetSelIndex();
  if (count <= 1 || index >= count-1)
    return;
 
  DeleteString(index);
  SetSelIndex(index);
  DirtyFlag = true;
}
 
//
/// Automatically responds to a menu selection with a menu ID of CM_EDITCLEAR by
/// clearing the items in the list box using functions in TListBox.
//
void
TListBoxView::CmEditClear()
{
  int count = GetCount();
  if (count == 1)
    return;
  if (count) {
    ClearList();
    DirtyFlag = true;
    SetHorizontalExtent(MaxWidth = 0);
  }
  long style = GetWindowLong(GWL_STYLE);
  if (!(style & LBS_SORT))
    TListBox::AddString(VirtualLastLineStr);
}
 
static int linePrompt(TWindow* parent, int index, UINT id,
                      LPTSTR buf, int buflen)
{
  tchar msg[41];
  _stprintf(msg, parent->LoadString(IDS_LISTNUM).c_str(), index);
  return TInputDialog(parent, msg, parent->LoadString(id).c_str(), buf, buflen).Execute();
}
 
//
/// Automatically responds to CM_LISTADD message by getting the length of the input
/// string and inserting the text string into the list view. Sets the data member
/// DirtyFlag to true.
//
void
TListBoxView::CmEditAdd()
{
  tchar inputText[101];
  *inputText = 0;
 
  int index = GetSelIndex();
  if (index < 0)
    index = 0;
 
  if (linePrompt(this,index+1,CM_EDITADD,inputText,COUNTOF(inputText)) == IDOK) {
    InsertString(inputText, index);
    SetSelIndex(index+1);
    SetExtent(inputText);
    DirtyFlag = true;
  }
}
 
//
/// Automatically responds to a CM_LISTEDIT message by getting the input text and
/// inserting the text into the list view. Sets DirtyFlag to a nonzero value to
/// indicate that the view has been changed and not saved.
//
void
TListBoxView::CmEditItem()
{
  int index = GetSelIndex();
  int count = GetCount();
 
  if (index == count-1) {
    CmEditAdd();
    return;
  }
 
  if (index < 0 || index >= count-1)
    return;
 
  tchar inputText[101];
  GetSelString(inputText, COUNTOF(inputText)-1);
 
  if (linePrompt(this,index+1,CM_EDITEDIT,inputText,COUNTOF(inputText))==IDOK) {
    DeleteString(index);
    InsertString(inputText, index);
    SetExtent(inputText);
    SetSelIndex(index);
    DirtyFlag = true;
  }
}
 
//
// To prevent interpreting as unprocessed accelerator
//
/// Automatically responds to a LBN_SELCHANGE message (which indicates that the
/// contents of the list view have changed) by calling TWindow::DefaultProcessing.
//
void
TListBoxView::CmSelChange()
{
}
 
 
IMPLEMENT_STREAMABLE2(TListBoxView, TListBox, TView);
 
#if OWL_PERSISTENT_STREAMS
 
//
//
//
void*
TListBoxView::Streamer::Read(ipstream& is, uint32 /*version*/) const
{
  ReadBaseObject((TListBox*)GetObject(), is);
  ReadBaseObject((TView*)GetObject(), is);
  is >> GetObject()->Origin;
  return GetObject();
}
 
//
//
//
void
TListBoxView::Streamer::Write(opstream &os) const
{
  WriteBaseObject((TListBox*)GetObject(), os);
  WriteBaseObject((TView*)GetObject(), os);
  os << GetObject()->Origin;
}
 
#endif
 
} // OWL namespace
/* ========================================================================== */
 

V601 The 'false' value is implicitly cast to the integer type.

V601 The 'false' value is implicitly cast to the integer type.

V601 The 'false' value is implicitly cast to the integer type.

V601 The 'true' value is implicitly cast to the integer type.

V601 The 'true' value is implicitly cast to the integer type.

V601 The 'true' value is implicitly cast to the integer type.

V601 The 'true' value is implicitly cast to the integer type.

V601 The 'true' value is implicitly cast to the integer type.