home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C++ / Applications / Nuntius 1.2 / src / Nuntius / UArticleView.cp < prev    next >
Encoding:
Text File  |  1994-04-13  |  10.5 KB  |  458 lines  |  [TEXT/MPS ]

  1. // Copyright © 1992 Peter Speck, speck@dat.ruc.dk. All rights reserved.
  2. // UArticleView.cp
  3.  
  4. #include "UArticleView.h"
  5. #include "UArticleCmds.h"
  6. #include "UArticle.h"
  7. #include "UArticleCache.h"
  8. #include "UPrefsDatabase.h"
  9. #include "UGroupDoc.h"
  10. #include "NetAsciiTools.h"
  11. #include "URowSelection.h"
  12. #include "Tools.h"
  13. #include "ViewTools.h"
  14. #include "UThread.h"
  15.  
  16. #include <RsrcGlobals.h>
  17.  
  18. #include <ToolUtils.h>
  19. #include <Packages.h>
  20.  
  21. #pragma segment MyArticle
  22.  
  23. #define qDebugArticleView qDebug & 0
  24. #define qDebugtabs qDebug & 0
  25.  
  26. const short kSubjectLine = 1;
  27. const short kFromLine = 2;
  28. const short kDateLine = 3;
  29. const short kHeaderBodySeparatorLine = 4;
  30. const short kNoShortHeaderLines = 4; // including header/body separator
  31.  
  32. TArticleView::TArticleView()
  33. {
  34. }
  35.  
  36. pascal void TArticleView::Initialize()
  37. {
  38.     inherited::Initialize();
  39.     fArticle = nil;
  40.     fDoc = nil;
  41.     fArticleID = -1;
  42.     fShowsAllHeaders = false;
  43.     fUseROT13 = false;
  44.     fUseJapaneseFont = false;
  45. }
  46.  
  47. void TArticleView::IArticleView(TGroupDoc *doc, TView *superView, VPoint location,
  48.                                         long articleID, TArticle *article)
  49. {
  50.     inherited::IGridView(doc, superView, location, VPoint(640, 0), sizeVariable, sizeVariable,
  51.         0, 1, 0, 640, kDontAdorn, kDontAdorn, 0, 0, false);
  52.     FailInfo fi;
  53.     if (fi.Try())
  54.     {
  55.         fCursorID = 3; // plusCursor = 3
  56.         fDoc = doc;
  57.         fArticleID = articleID;
  58.         fArticle = article;
  59.  
  60.         fUseJapaneseFont = fArticle->ContainsJapaneseEncoding();
  61.         StandardGridViewTextStyle gvts;
  62.         if (fUseJapaneseFont)
  63.             MAGetTextStyle(kJapaneseFontTextStyle, gvts.fTextStyle);
  64.         else
  65.             gPrefs->GetTextStylePrefs('TSar', gvts.fTextStyle);
  66.         CalcStandardGridViewFont(gvts);
  67.         fGridViewTextStyle = gvts;
  68.  
  69.         fShowsAllHeaders = gPrefs->GetBooleanPrefs('Head');
  70.         long noLines, bodyStartLineNo;
  71.         fArticle->GetLineInfo(noLines, bodyStartLineNo);
  72.         fBodyStartLineNo = bodyStartLineNo;
  73.         ArrayIndex noRows;
  74.         if (fShowsAllHeaders)
  75.             noRows = noLines;
  76.         else
  77.         {
  78.             ArrayIndex noBodyLines = noLines - bodyStartLineNo + 1;
  79.             noRows = kNoShortHeaderLines + noBodyLines;
  80.         }
  81.         InsRowLast(short(noRows), fGridViewTextStyle.fRowHeight);
  82. #if qDebugArticleView
  83.         fprintf(stderr, "TArticleView: inserted %ld rows of height %ld, article = %ld\n", noRows, long(fGridViewTextStyle.fRowHeight), articleID);
  84. #endif
  85.         
  86.         fDoc->GetArticleStatus()->SetMinStatus(articleID, kArticleRead);
  87.         
  88.         fi.Success();
  89.     }
  90.     else // fail
  91.     {
  92.         Free();
  93.         fi.ReSignal();
  94.     }
  95. }
  96.  
  97. pascal void TArticleView::ReadFields(TStream *aStream)
  98. {
  99.     inherited::ReadFields(aStream);
  100. }
  101.  
  102. pascal void TArticleView::DoPostCreate(TDocument *itsDocument)
  103. {
  104.     inherited::DoPostCreate(itsDocument);
  105.     fDoc = (TGroupDoc*) itsDocument;
  106.  
  107.     StandardGridViewTextStyle gvts;
  108.     gPrefs->GetTextStylePrefs('TSar', gvts.fTextStyle);
  109.     CalcStandardGridViewFont(gvts);
  110.     fGridViewTextStyle = gvts;
  111. }
  112.  
  113. pascal void TArticleView::Free()
  114. {
  115.     gArticleCache->ReturnArticle(fArticle); fArticle = nil;
  116.     fDoc = nil; // not mine
  117.     inherited::Free();
  118. }
  119.  
  120. pascal void TArticleView::Close()
  121. {
  122.     inherited::Close();
  123. }
  124.  
  125. void TArticleView::SetNewFont(const TextStyle &textStyle)
  126. {
  127.     StandardGridViewTextStyle gvts;
  128.     gvts.fTextStyle = textStyle;
  129.     CalcStandardGridViewFont(gvts);
  130.     fGridViewTextStyle = gvts;
  131.     if (fNumOfRows)
  132.         SetRowHeight(1, fNumOfRows, fGridViewTextStyle.fRowHeight);
  133.     Focus();
  134.     ForceRedraw();
  135. }
  136.  
  137. void TArticleView::SetUseROT13(Boolean useROT13)
  138. {
  139.     if (fUseROT13 == useROT13)
  140.         return;
  141.     fUseROT13 = useROT13;
  142.     Focus();
  143.     ForceRedraw();
  144. }
  145.  
  146. void TArticleView::UpdateDisplay()
  147. {
  148.     Boolean doShowHeaders = gPrefs->GetBooleanPrefs('Head');
  149.     if (fShowsAllHeaders != doShowHeaders)
  150.     {
  151.         ArrayIndex diff = fBodyStartLineNo - (kNoShortHeaderLines + 1);
  152.         if (fShowsAllHeaders)
  153.             diff = -diff;
  154.         DirectDeltaRows(this, kNoShortHeaderLines + 1, short(diff), fGridViewTextStyle.fRowHeight);
  155.         fShowsAllHeaders = doShowHeaders;
  156.         VRect vr1, vr2;
  157.         CellToVRect(GridCell(1, 1), vr1);
  158.         CellToVRect(GridCell(1, kNoShortHeaderLines), vr2);
  159.         Focus();
  160.         InvalidateVRect(vr1 | vr2);
  161.     }
  162. }
  163. //============================================== DRAWING ================================
  164. pascal void TArticleView::DrawRangeOfCells(GridCell startCell,
  165.                                         GridCell stopCell,
  166.                                         const VRect &aRect)
  167. {
  168. #if qDebug
  169.     if (!fArticle)
  170.         ProgramBreak("DrawRangeOfCells called when having no TArticle");
  171. #endif
  172.     TextStyle itsTextStyle = fGridViewTextStyle.fTextStyle;
  173.     SetPortTextStyle(itsTextStyle);
  174.     inherited::DrawRangeOfCells(startCell, stopCell, aRect);
  175. }
  176.  
  177. pascal void TArticleView::DrawCell(GridCell aCell, const VRect &aRect)
  178. {
  179.     CStr255 text;
  180.     GetLineText(aCell.v, false, text);
  181.     if (!text.Length())
  182.         return;
  183.     CRect r;
  184.     ViewToQDRect(aRect, r);
  185.     MoveTo(r.left + 2, r.top + fGridViewTextStyle.fVertOffset);
  186.     // expand tabs to spaces
  187.     text += char(0);
  188.     unsigned char *p = &text[1];
  189.     CStr255 s;
  190.     s.Length() = 0;
  191.     unsigned char ch = 0;
  192.     while ((ch = *p++) != 0)
  193.     {
  194.         if (ch == chTab)
  195.         {
  196.             do
  197.             {
  198. #if qDebugtabs
  199.                 s += '·'; // small dot in Courier
  200. #else
  201.                 s += char(chSpace);
  202. #endif
  203.             } while ((s.Length() & 7) != 0);
  204.         }
  205.         else if (ch < 32)
  206.         {
  207.             s += '^';
  208.             s += char(ch + 'A');
  209.         }
  210.         else
  211.             s += ch;
  212.     }
  213.     DrawString(s);
  214. }
  215.  
  216. void TArticleView::GetLineText(short lineNo, Boolean asQuote, CStr255 &text)
  217. {
  218.     if (DoGetLineText(lineNo, asQuote, text) == false)
  219.         return;
  220.     if (!text.Length())
  221.         return;
  222.     if (!fUseJapaneseFont)
  223.     {
  224.         TranslateCStr255(text, gNetAscii2Mac);
  225.         if (fUseROT13)
  226.             TranslateCStr255(text, gROT13Table);
  227.     }
  228. }
  229.  
  230. // DoGetLineText returns false if the text already have been char-translated.
  231. Boolean TArticleView::DoGetLineText(short lineNo, Boolean asQuote, CStr255 &text)
  232. {
  233.     if (fShowsAllHeaders)
  234.     {
  235.         fArticle->GetLine(lineNo, text);
  236.         if (asQuote)
  237.         {
  238.             CStr255 s;
  239.             MyGetIndString(s, kQuoteLineTemplate);
  240.             SubstituteStringItems(s, "«line»", text);
  241.             text = s;
  242.         }
  243.         return true;
  244.     }
  245.     if (lineNo > kNoShortHeaderLines)
  246.     {
  247.         fArticle->GetLine(lineNo - kNoShortHeaderLines - 1 + fBodyStartLineNo, text);
  248.         if (asQuote)
  249.             text.Insert(">", 1);
  250.         return true;
  251.     }
  252.     CStr255 s, email;
  253.     HandleOffsetLength hol;
  254.     GridCell firstCell = FirstSelectedCell();
  255.     GridCell lastCell  = LastSelectedCell();
  256.     switch (lineNo)
  257.     {
  258.         case kSubjectLine:
  259.             fDoc->GetSubject(fArticleID, hol);
  260.             CopyHolToCStr255(hol, text);
  261.             if (asQuote && firstCell == lastCell)
  262.                 break;
  263.             MyGetIndString(s, kPreSubject);
  264.             text.Insert(s, 1);
  265.             MyGetIndString(s, kPostSubject);
  266.             text += s;
  267. #if qDebug
  268.             if (!asQuote)
  269.             {
  270.                 text += ", ";
  271.                 NumToString(fArticleID, s);
  272.                 text += s;
  273.             }
  274. #endif
  275.             return false;
  276.  
  277.         case kFromLine:
  278.             fDoc->GetFrom(fArticleID, hol);
  279.             CopyHolToCStr255(hol, text);
  280.             Boolean hadRealName = GetPrintableAuthorName(text, s, email);
  281.             if (asQuote && firstCell == lastCell)
  282.             {
  283.                 text = email;
  284.                 if (hadRealName)
  285.                     text += " (" + s + ")"; // RFC-822 style
  286.                 break;
  287.             }
  288.             MyGetIndString(text, kPreFrom);
  289.             text += s;
  290.             if (hadRealName)
  291.             {
  292.                 MyGetIndString(s, kArticleReal2EmailSeparator);
  293.                 text += s;
  294.                 text += email;
  295.             }
  296.             MyGetIndString(s, kPostFrom);
  297.             text += s;
  298.             return false;
  299.  
  300.         case kDateLine:
  301.             MyGetIndString(text, kPreDate);
  302.             fArticle->GetHeader("Date", s);
  303.             text += s;
  304.             MyGetIndString(s, kPostDate);
  305.             text += s;
  306.             return false;
  307.             
  308.         case kHeaderBodySeparatorLine:
  309.             if (asQuote)
  310.                 GetQuoteFromTextLine(text, kArticleQuoteLine);
  311.             else
  312.                 text = "";
  313.             return false;
  314.     } // case
  315. }
  316.  
  317. void TArticleView::GetQuoteFromTextLine(CStr255 &text, short quoteTemplateID)
  318. {
  319.     MyGetIndString(text, quoteTemplateID);
  320.     //
  321.     CStr255 s, s2, s3;
  322.     //
  323.     fArticle->GetHeader("Message-id", s);
  324.     SubstituteStringItems(text, "«msgid»", s);
  325.     //
  326.     fArticle->GetHeader("From", s);
  327.     GetAuthorName(s, s2, s3);
  328.     SubstituteStringItems(text, "«realname»", s2);
  329.     SubstituteStringItems(text, "«email»", s3);
  330.     //
  331.     fArticle->GetHeader("Date", s);
  332.     SubstituteStringItems(text, "«date»", s);
  333.     //
  334.     char mailboxdate[60];
  335.     DateTimeRec dtr;
  336.     GetTime(dtr);
  337.     GetIndString(s, kEnglishShortMonthStrings, dtr.month);
  338.     GetIndString(s2, kEnglishShortWeekdayStrings, dtr.dayOfWeek);
  339.     sprintf(mailboxdate, "%3s %3s %2hd %02hd:%02hd:%02hd %hd",
  340.         (char*)s2, (char*)s, dtr.day,
  341.         dtr.hour, dtr.minute, dtr.second, dtr.year);
  342.     SubstituteStringItems(text, "«mailboxdate»", mailboxdate);
  343. }
  344.  
  345. void TArticleView::AppendSelectionAsTextForClipboard(Handle h, Boolean asQuote, 
  346.                                                             Boolean addHeader, short quoteTemplateID)
  347. {
  348.     if (!IsAnyCellSelected())
  349.         return;
  350.     FailInfo fi;
  351.     if (fi.Try())
  352.     {
  353.         CStr255 text;
  354.         Boolean gotHeader = !addHeader; 
  355.         // if we don't want the header, make it look like we have already added it
  356.         ArrayIndex sepLine = (fShowsAllHeaders ? fBodyStartLineNo - 1 : kNoShortHeaderLines);
  357.         CSelectedCellIterator iter(this);
  358.         for (GridCell aCell = iter.FirstCell(); iter.More(); aCell = iter.NextCell())
  359.         {
  360.             if (!gotHeader)
  361.             {
  362.                 if (aCell.v >= sepLine)
  363.                 {
  364.                     GetQuoteFromTextLine(text, quoteTemplateID);
  365.                     if (!text.Length() || text[text.Length()] != 13)
  366.                         text += "\n";
  367.                     AppendStringToHandle(text, h);
  368.                     if (aCell.v == sepLine)
  369.                         continue;
  370.                 }
  371.                 gotHeader = true;
  372.             }
  373.             GetLineText(aCell.v, asQuote, text);
  374.             text += "\n";
  375.             AppendStringToHandle(text, h);
  376.         }
  377.         fi.Success();
  378.     }
  379.     else // fail
  380.     {
  381.         fi.ReSignal();
  382.     }
  383. }
  384.  
  385. long TArticleView::GetArticleID()
  386. {
  387.     return fArticleID;
  388. }
  389.  
  390. TArticle *TArticleView::GetArticle()
  391. {
  392.     return fArticle;
  393. }
  394.  
  395. //======================== MANAGEMENT ==================================================
  396. pascal void TArticleView::DoMouseCommand(VPoint &theMouse,
  397.                                                         TToolboxEvent *event, CPoint /* hysteresis */ )
  398. {
  399.     GridCell aCell;
  400.     if (IdentifyPoint(theMouse, aCell) == badChoice)
  401.         return;
  402.     if (event->fClickCount > 1 && IsAnyCellSelected())
  403.     {
  404.         // OpenSelection();
  405.     }
  406.     else // not double-click
  407.     {
  408.         if (IsAnyCellSelected() && FirstSelectedCell().v == 1 && LastSelectedCell().v == fNumOfRows && !event->IsShiftKeyPressed() && !event->IsCommandKeyPressed())
  409.         {
  410.             // against any HIG, but users want it
  411.             SetEmptySelection(kHighlight);
  412.             return;
  413.         }
  414.         TStickySelectCommand *aCommand = new TStickySelectCommand();
  415.         aCommand->IStickySelectCommand(this, theMouse, event->IsShiftKeyPressed(), event->IsCommandKeyPressed());
  416.         PostCommand(aCommand);
  417.     }
  418. }
  419.  
  420.  
  421. pascal void TArticleView::DoKeyEvent(TToolboxEvent *event)// override 
  422. {
  423.     if (!IsEnabled())
  424.     {
  425.         inherited::DoKeyEvent(event);
  426.         return;
  427.     }
  428.     switch (event->fCharacter)
  429.     {
  430.         case chBackspace:
  431.             SetEmptySelection(kRedraw);
  432.             break;
  433.  
  434.         default:
  435.             inherited::DoKeyEvent(event);
  436.             break;
  437.     }
  438. }
  439.  
  440. pascal void TArticleView::DoMenuCommand(CommandNumber aCommandNumber)
  441. {
  442.     if (!IsEnabled())
  443.     {
  444.         inherited::DoMenuCommand(aCommandNumber);
  445.         return;
  446.     }
  447.     switch (aCommandNumber)
  448.     {
  449.         default:
  450.             inherited::DoMenuCommand(aCommandNumber);
  451.     }
  452. }
  453.  
  454. pascal void TArticleView::DoSetupMenus()
  455. {
  456.     inherited::DoSetupMenus();
  457. }
  458.