Skein::Node* TranscriptWindow::FindRelevantNode(FindAction action, bool next, bool selected)
{
    // Get all the nodes in the transcript or skein
    CArray<Skein::Node*,Skein::Node*> nodes;
    if (action == SkeinDifferent)
    {
        ASSERT(next);
        m_skein->GetAllNodes(nodes);
    }
    else
    {
        Skein::Node* node = m_skeinEndThread;
        while (node != NULL)
        {
            if (next)
                nodes.InsertAt(0,node);
            else
                nodes.Add(node);
            node = node->GetParent();
        }
    }
    if (next)
        nodes.InsertAt(0,(Skein::Node*)NULL);

    // Return the next relevant node after the selected node (which may be NULL)
    bool afterSelected = false;
    for (int i = 0; i < nodes.GetSize(); i++)
    {
        if (afterSelected == false)
        {
            if (nodes.GetAt(i) == (selected ? m_skeinSelected : NULL))
                afterSelected = true;
        }
        else
        {
            switch (action)
            {
            case TranscriptDifferent:
            case SkeinDifferent:
                if (nodes.GetAt(i)->GetDiffers() != Skein::Node::ExpectedSame)
                    return nodes.GetAt(i);
                break;
            case TranscriptChanged:
                if (nodes.GetAt(i)->GetChanged())
                    return nodes.GetAt(i);
                break;
            default:
                ASSERT(FALSE);
                break;
            }
        }
    }

    // If nothing found, then if searching the whole skein, start from the beginning
    if ((action == SkeinDifferent) && selected)
        return FindRelevantNode(action,next,false);
    return NULL;
}
예제 #2
0
void SkeinWindow::OnLButtonUp(UINT nFlags, CPoint point)
{
  Skein::Node* node = NodeAtPoint(point);
  if (node != NULL)
  {
    // Is the user clicking on the "differs badge"?
    if ((node->GetDiffers() != Skein::Node::ExpectedSame) && (node->GetExpectedText().IsEmpty() == FALSE))
    {
      CSize badgeSize = m_bitmaps[DiffersBadge]->GetSize();
      CRect badgeRect = m_nodes[node];
      badgeRect.left = badgeRect.right-badgeSize.cx;
      badgeRect.top = badgeRect.bottom-badgeSize.cy;
      if (badgeRect.PtInRect(point))
        GetParentFrame()->SendMessage(WM_SHOWTRANSCRIPT,(WPARAM)node,(LPARAM)GetSafeHwnd());
    }
  }

  CScrollView::OnLButtonUp(nFlags,point);
}
void TranscriptWindow::Layout(void)
{
    CRect clientRect;
    GetClientRect(clientRect);

    m_layout.clientSize = clientRect.Size();
    m_layout.columnWidth = clientRect.Width()/2;

    m_layout.font = theApp.GetFont(InformApp::FontDisplay);
    m_layout.fontSize = theApp.MeasureFont(m_layout.font);
    m_layout.margin = CSize(m_layout.fontSize.cx,m_layout.fontSize.cy/3);
    m_layout.centreMargin = m_layout.margin.cx*4;

    CDC* dc = GetDC();

    m_layout.nodes.clear();
    Skein::Node* node = m_skeinEndThread;
    while (node != NULL)
    {
        NodeLayout nl;
        nl.node = node;

        // Get the text associated with the node
        const CStringW& transcript = nl.node->GetTranscriptText();
        const CStringW& expected = nl.node->GetExpectedText();

        // Measure the height of the expected text
        CRect textRect;
        textRect.SetRectEmpty();
        textRect.right = m_layout.columnWidth-m_layout.margin.cx-m_layout.centreMargin;
        SizeText(*dc,textRect,expected);
        nl.height = textRect.Height();

        // Measure the height of the transcript text
        textRect.SetRectEmpty();
        textRect.right = m_layout.columnWidth-m_layout.margin.cx-m_layout.centreMargin;
        SizeText(*dc,textRect,transcript);
        if (textRect.Height() > nl.height)
            nl.height = textRect.Height();

        // Use the tallest for the height of the node in the transcript
        if (m_layout.fontSize.cy > nl.height)
            nl.height = m_layout.fontSize.cy;

        m_layout.nodes.push_front(nl);
        node = node->GetParent();
    }

    ReleaseDC(dc);

    // Compare the height of the transcript with the height of the window
    int height = GetHeight();
    if (height > clientRect.Height())
    {
        // The transcript is taller than the window, so turn the scrollbar on
        EnableScrollBar(SB_VERT,ESB_ENABLE_BOTH);

        // Get the current scrollbar settings
        SCROLLINFO scroll;
        ::ZeroMemory(&scroll,sizeof scroll);
        scroll.cbSize = sizeof scroll;
        GetScrollInfo(SB_VERT,&scroll);

        // Change the maximum position and the size of the scrollbar
        scroll.nMin = 0;
        scroll.nMax = height-1;
        scroll.nPage = clientRect.Height();
        SetScrollInfo(SB_VERT,&scroll);
    }
    else
    {
        // The transcript is shorter than the window, so turn the scrollbar off
        EnableScrollBar(SB_VERT,ESB_DISABLE_BOTH);

        SCROLLINFO scroll;
        ::ZeroMemory(&scroll,sizeof scroll);
        scroll.cbSize = sizeof scroll;
        SetScrollInfo(SB_VERT,&scroll);
    }
}
예제 #4
0
void SkeinWindow::DrawNode(Skein::Node* node, CDC& dc, CDibSection& bitmap, const CRect& client,
  const CPoint& centre)
{
  // Store the current device context properties
  UINT align = dc.GetTextAlign();
  int mode = dc.GetBkMode();

  // Set the device context properties
  dc.SetTextAlign(TA_TOP|TA_LEFT);
  dc.SetBkMode(TRANSPARENT);

  // Get the text associated with the node
  LPCWSTR line = node->GetLine();
  LPCWSTR label = node->GetLabel();
  int width = node->GetLineWidth(dc,&m_labelFont);

  // Check if this node is visible before drawing
  CRect nodeArea(centre,CSize(width+m_fontSize.cx*10,m_fontSize.cy*3));
  nodeArea.OffsetRect(nodeArea.Width()/-2,nodeArea.Height()/-2);
  CRect intersect;
  if (intersect.IntersectRect(client,nodeArea))
  {
    CDibSection* back = m_bitmaps[BackUnplayed];

    bool gameRunning = GetParentFrame()->SendMessage(WM_GAMERUNNING) != 0;
    Skein::Node* playNode = gameRunning ? m_skein->GetPlayed() : m_skein->GetCurrent();
    while (playNode != NULL)
    {
      if (playNode == node)
      {
        back = m_bitmaps[BackPlayed];
        break;
      }
      playNode = playNode->GetParent();
    }

    if (node->GetExpectedText().IsEmpty())
    {
      if (back == m_bitmaps[BackPlayed])
        back = m_bitmaps[BackPlayedDark];
      else if (back == m_bitmaps[BackUnplayed])
        back = m_bitmaps[BackUnplayedDark];
    }

    // Draw the node's background
    DrawNodeBack(node,dc,bitmap,centre,width,back);

    // Write out the node's line
    int textWidth = node->GetLineTextWidth();
    ::ExtTextOutW(dc.GetSafeHdc(),centre.x-(textWidth/2),centre.y-(m_fontSize.cy/2),0,
      NULL,line,(UINT)wcslen(line),NULL);

    // Write out the node's label, if any
    if (ShowLabel(node))
    {
      CFont* oldFont = dc.SelectObject(&m_labelFont);
      SIZE size;
      ::GetTextExtentPoint32W(dc.GetSafeHdc(),label,(UINT)wcslen(label),&size);
      ::ExtTextOutW(dc.GetSafeHdc(),centre.x-(size.cx/2),centre.y-(int)(2.1*m_fontSize.cy),
        0,NULL,label,(UINT)wcslen(label),NULL);
      dc.SelectObject(oldFont);
    }
  }

  // Reset the device context properties
  dc.SetTextAlign(align);
  dc.SetBkMode(mode);
}
예제 #5
0
void SkeinWindow::OnContextMenu(CWnd* pWnd, CPoint point)
{
  // No menu if currently editing
  if (m_edit.IsWindowVisible())
    return;

  // No menu if running the game
  bool gameRunning = GetParentFrame()->SendMessage(WM_GAMERUNNING) != 0;
  bool gameWaiting = GetParentFrame()->SendMessage(WM_GAMEWAITING) != 0;
  if (gameRunning && !gameWaiting)
    return;

  // Find the node under the mouse, if any
  CPoint cp(point);
  ScreenToClient(&cp);
  Skein::Node* node = NodeAtPoint(cp);
  if (node == NULL)
    return;

  // Get the context menu
  CMenu popup;
  popup.LoadMenu(IDR_SKEIN);
  CMenu* menu = popup.GetSubMenu(0);

  // Update the state of the context menu items
  if (node->GetParent() == NULL)
  {
    menu->RemoveMenu(ID_SKEIN_EDIT,MF_BYCOMMAND);
    menu->RemoveMenu(ID_SKEIN_ADD_LABEL,MF_BYCOMMAND);
    menu->RemoveMenu(ID_SKEIN_EDIT_LABEL,MF_BYCOMMAND);
    menu->RemoveMenu(ID_SKEIN_INSERT_KNOT,MF_BYCOMMAND);
    menu->RemoveMenu(ID_SKEIN_DELETE,MF_BYCOMMAND);
    menu->RemoveMenu(ID_SKEIN_DELETE_BELOW,MF_BYCOMMAND);
    menu->RemoveMenu(ID_SKEIN_DELETE_THREAD,MF_BYCOMMAND);
    menu->RemoveMenu(ID_SKEIN_LOCK,MF_BYCOMMAND|MF_GRAYED);
    menu->RemoveMenu(ID_SKEIN_UNLOCK,MF_BYCOMMAND|MF_GRAYED);
    menu->RemoveMenu(ID_SKEIN_LOCK_THREAD,MF_BYCOMMAND|MF_GRAYED);
    menu->RemoveMenu(ID_SKEIN_UNLOCK_THREAD,MF_BYCOMMAND|MF_GRAYED);
  }
  else
  {
    if (gameRunning && m_skein->InCurrentThread(node))
    {
      menu->EnableMenuItem(ID_SKEIN_DELETE,MF_BYCOMMAND|MF_GRAYED);
      menu->EnableMenuItem(ID_SKEIN_DELETE_BELOW,MF_BYCOMMAND|MF_GRAYED);
    }
    if (gameRunning && m_skein->InCurrentThread(m_skein->GetThreadTop(node)))
      menu->EnableMenuItem(ID_SKEIN_DELETE_THREAD,MF_BYCOMMAND|MF_GRAYED);
    if (node->GetLabel().IsEmpty())
      menu->RemoveMenu(ID_SKEIN_EDIT_LABEL,MF_BYCOMMAND);
    else
      menu->RemoveMenu(ID_SKEIN_ADD_LABEL,MF_BYCOMMAND);
  }
  if (node->GetTemporary())
  {
    menu->RemoveMenu(ID_SKEIN_UNLOCK,MF_BYCOMMAND|MF_GRAYED);
    menu->RemoveMenu(ID_SKEIN_UNLOCK_THREAD,MF_BYCOMMAND|MF_GRAYED);
  }
  else
  {
    menu->RemoveMenu(ID_SKEIN_LOCK,MF_BYCOMMAND|MF_GRAYED);
    menu->RemoveMenu(ID_SKEIN_LOCK_THREAD,MF_BYCOMMAND|MF_GRAYED);
  }

  // Show the context menu
  int cmd = menu->TrackPopupMenuEx(TPM_LEFTALIGN|TPM_TOPALIGN|TPM_NONOTIFY|TPM_RETURNCMD,
    point.x,point.y,GetParentFrame(),NULL);

  // Act on the context menu choice
  switch (cmd)
  {
  case ID_SKEIN_PLAY:
    GetParentFrame()->SendMessage(WM_PLAYSKEIN,(WPARAM)node);
    break;
  case ID_SKEIN_EDIT:
    StartEdit(node,false);
    break;
  case ID_SKEIN_ADD_LABEL:
  case ID_SKEIN_EDIT_LABEL:
    // Make sure that the label background is visible
    Invalidate();
    StartEdit(node,true);
    break;
  case ID_SKEIN_TRANSCRIPT:
    GetParentFrame()->SendMessage(WM_SHOWTRANSCRIPT,(WPARAM)node,(LPARAM)GetSafeHwnd());
    break;
  case ID_SKEIN_LOCK:
    m_skein->Lock(node);
    break;
  case ID_SKEIN_UNLOCK:
    m_skein->Unlock(node);
    break;
  case ID_SKEIN_LOCK_THREAD:
    m_skein->Lock(m_skein->GetThreadBottom(node));
    break;
  case ID_SKEIN_UNLOCK_THREAD:
    m_skein->Unlock(m_skein->GetThreadTop(node));
    break;
  case ID_SKEIN_NEW_THREAD:
    {
      Skein::Node* newNode = m_skein->AddNew(node);

      // Force a repaint so that the new node is drawn and recorded
      Invalidate();
      UpdateWindow();
      StartEdit(newNode,false);
    }
    break;
  case ID_SKEIN_INSERT_KNOT:
    {
      Skein::Node* newNode = m_skein->AddNewParent(node);
      Invalidate();
      UpdateWindow();
      StartEdit(newNode,false);
    }
    break;
  case ID_SKEIN_DELETE:
    if (CanRemove(node))
      m_skein->RemoveSingle(node);
    break;
  case ID_SKEIN_DELETE_BELOW:
    if (CanRemove(node))
      m_skein->RemoveAll(node);
    break;
  case ID_SKEIN_DELETE_THREAD:
    {
      Skein::Node* topNode = m_skein->GetThreadTop(node);
      if (CanRemove(topNode))
        m_skein->RemoveAll(topNode);
    }
    break;
  case ID_SKEIN_SAVE_TRANSCRIPT:
    {
      SimpleFileDialog dialog(FALSE,"txt",NULL,OFN_HIDEREADONLY|OFN_ENABLESIZING,
        "Text Files (*.txt)|*.txt|All Files (*.*)|*.*||",this);
      dialog.m_ofn.lpstrTitle = "Save the transcript up to this knot";
      if (dialog.DoModal() == IDOK)
        m_skein->SaveTranscript(node,dialog.GetPathName());
    }
    break;
  case 0:
    // Make sure this window still has the focus
    SetFocus();
    break;
  }
}