/* * This file contains miscellaneous MediaView handling code. */ #include #include #include #include #include #include #include #include /* MediaView Include Files */ #include #include /* Application Include Files */ #include "mvui.h" #include "view.h" #include "pane.h" #include "resrc1.h" #include "proto.h" /* The history list. */ typedef struct tagHISTORY { int inUse; /* in use flag ... hName might be NULL */ VA va; /* topic address */ long scroll; /* the scroll position */ int isName; /* is the hName a name handle or a topic number? */ HANDLE hName; /* topic name */ } HISTORY; HISTORY History[NUMHISTORY] = {0}; HISTORY Bookmarks[NUMBOOKMARKS] = {0}; int iH = -1; /* History index */ int iB = -1; /* back history index */ int iBM = -1; /* next free Bookmark */ LPPRINTMARK PrintMarks = 0; int iPM = -1; /**************************************************************************** ** FUNCTION: MV_SourceUpdate ** ** PURPOSE: Update the source window to display the current topic ** ** bitmap. ** ** COMMENTS: ** ** It is the responsibility of the application to free the memory ** ** containing the returned title string. ** ****************************************************************************/ void MV_SourceUpdate(LPMV lpMV) { HANDLE hMem, hdl; LPSTR lp; long topicNumber; char buff[20]; VA va; extern VIEW SourceView; /* the open View of the SOURCES.M12 */ extern HWND hSourceWnd; /* the window to show it in */ /* if the source window is not visible, don't bother */ if (!SourceView.isOpen || !SourceView.lpApp->showSource) return; /* get the title (the $ footnote) */ hMem = hMVGetName(lpMV); /* set the window title */ if (hMem) lp = GlobalLock(hMem); else { /* There was no '$' footnote ... create a topic number string */ topicNumber = lMVTopicNumber(lpMV); wsprintf(buff, "Topic #%ld", topicNumber); lp = buff; } SetWindowText(hSourceWnd, lp); /* * Set the topic in the source window. The data is authored into the * USA title with the '!' footnote and contains a context-string for * the SOURCES title. */ hdl = hMVGetData(lpMV); if (hdl) { lp = GlobalLock(hdl); /* * We have the context string ... turn it into an address and * set the topic in the SOURCES title. The display update, etc. * happens by setting the topic. The SET_FROM_SOURCE prevents * this routine from being called recursively. */ va = vaConvertContextString(SourceView.hTitle, lp); View_SetTopic(&SourceView, va, 0, SET_FROM_SOURCE); GlobalFree(hdl); } /* the application must free the memory */ if (hMem) GlobalFree(hMem); } /**************************************************************************** ** FUNCTION: MV_Search ** ** PURPOSE: Full text search for the string pattern. ** ** COMMENTS: ** ****************************************************************************/ int MV_Search(LPVIEW lpView, int scope, int andFlag, char ** groupList, char * szSearch) { int flag; HTLIST htList, htList1, htList2; QUERYERR qe; long topicNumber; /* clean up from previous search */ if (lpView->hTopicList) TopicListDestroy(lpView->hTopicList); /* AND or OR multiple search words? */ if (andFlag) flag = IMPLICIT_AND; else flag = IMPLICIT_OR; switch (scope) { case SEARCH_TOPIC: /* * search current topic: get the current topic number, * create a 1 topic list, and search */ topicNumber = lMVTopicNumber(View_ValidMV(lpView)); htList = TopicListFromTopicNo(lpView->hTitle, topicNumber); lpView->hTopicList = TopicListFromQuery(lpView->hTitle, (WORD)flag, szSearch, htList, 0, 0, &lpView->hHighlights, &qe); /* clean up the interim topic list */ TopicListDestroy(htList); break; case SEARCH_GROUPS: /* * Search all chosen groups: * for each group in the groupList * get the topic list and merge into the main list * do the search on the entire combined topic list */ htList = htList1 = htList2 = 0; /* find first group with topics */ while (*groupList && htList == 0) htList = TopicListLoad(lpView->hTitle, *groupList++); while (*groupList) { htList1 = htList; /* if there are any topics, combine them */ if ((htList2 = TopicListLoad(lpView->hTitle, *groupList)) != 0) { htList = TopicListCombine(TL_OR, htList1, htList2, 0); /* be sure to clean up the intermediate lists */ TopicListDestroy(htList1); TopicListDestroy(htList2); } ++groupList; } /* if there are any topics to search, then do so */ if (htList) { lpView->hTopicList = TopicListFromQuery(lpView->hTitle, (WORD)flag, szSearch, htList, 0, 0, &lpView->hHighlights, &qe); TopicListDestroy(htList); } else { /* otherwise set up the error return */ lpView->hTopicList = 0; return(ERR_FAILED); } break; case SEARCH_ALL: /* search all topics in the title */ lpView->hTopicList = TopicListFromQuery(lpView->hTitle, (WORD)flag, szSearch, 0, 0, 0, &lpView->hHighlights, &qe); break; } /* return 0 if there were any Search hits */ if (lpView->hTopicList == 0) return(qe.iError); else if (TopicListLength(lpView->hTopicList) == 0) return(ERR_FAILED); else return(0); } /**************************************************************************** ** FUNCTION: MV_ShowSearchResults ** ** PURPOSE: Load up the listbox with the search results. ** ** COMMENTS: ** ** The caller must make sure that the Search results dialog is ** ** active. ****************************************************************************/ void MV_ShowSearchResults(LPVIEW lpV) { long count, i, topicNumber; char buff[512]; HWND hList; /* get the handle of the hits display list */ hList = GetDlgItem(lpV->lpApp->hSearch, IDC_LIST1); SendMessage(hList, LB_RESETCONTENT, 0, 0); /* has there been a search? */ if (lpV->hTopicList == 0) return; /* for each topic number */ count = TopicListLength(lpV->hTopicList); for (i = 0; i < count; ++i) { topicNumber = TopicListLookup(lpV->hTopicList, i); TitleGetInfo(lpV->hTitle, TTLINF_TOPICTITLE, topicNumber, (long)(LPSTR)buff); /* and add it to the list box */ SendMessage(hList, LB_ADDSTRING, 0, (long)(LPSTR)buff); } /* default selection to first item */ SendMessage(hList, LB_SETCURSEL, 0, 0); } /**************************************************************************** ** FUNCTION: MV_GoToFromSearchResults ** ** PURPOSE: Get the results from the list box and set the topic ** ** COMMENTS: ** ****************************************************************************/ void MV_GoToFromSearchResults(HWND hDlg, LPVIEW lpV) { int selNumber; long topicNumber; VA va; /* convert selection -> topic number -> VA */ selNumber = (int)SendMessage( hDlg, LB_GETCURSEL, 0, 0); topicNumber = TopicListLookup(lpV->hTopicList, selNumber); va = vaConvertTopicNumber(lpV->hTitle, topicNumber); /* and go there */ View_SetTopic(lpV, va, 0, SET_FROM_OTHER); } /**************************************************************************** ** FUNCTION: MV_KeywordGroups ** ** PURPOSE: Load up the COMBO box with Keyword groups ** ** COMMENTS: ** ****************************************************************************/ int MV_KeywordGroups(LPVIEW lpV, HWND hGroups) { LPGROUPUI lpK = lpV->lpKeyIndex; int i = 0; /* if there are no keyword groups, disable the COMBO box */ if (!lpK || lpK->title == 0) { EnableWindow(hGroups, FALSE); return(0); } while (lpK && lpK->title) { SendMessage(hGroups, CB_ADDSTRING, 0, (LPARAM)lpK->title); /* Keyword group names are single letters */ SendMessage(hGroups, CB_SETITEMDATA, i, (LPARAM) *lpK->name); ++lpK; ++i; } /* default to the previous selection (if any) */ if (lpV->iIndex == -1) i = 0; else i = lpV->iIndex; SendMessage(hGroups, CB_SETCURSEL, i, 0); return(0); } /**************************************************************************** ** FUNCTION: MV_Keywords ** ** PURPOSE: Load up the dialog with Keywords from the selected group ** ** COMMENTS: ** ** The keywords for group 'a' are kept in Word Wheel '|a', etc. ** ****************************************************************************/ int MV_Keywords(LPVIEW lpV, HWND hList, HWND hGroups) { char buff[_MAX_PATH+1]; int numWords, i; long dw; char * WWfile = "|0"; /* open the WordWheel associated with the KeyIndex group */ i = (int)SendMessage(hGroups, CB_GETCURSEL, 0, 0); SendMessage(hList, LB_RESETCONTENT, 0, 0); /* there may not be any groups ... i == -1 defaults to |0, the default group */ if (i != -1 && i == lpV->iIndex) return(0); /* close the previously open WW file */ if (lpV->hWordWheel) WordWheelClose(lpV->hWordWheel); /* one at a time is kept open */ dw = SendMessage(hGroups, CB_GETITEMDATA, i, 0); if (i == -1) WWfile[1] = '0'; else WWfile[1] = (char)dw; /* the keyword lists are stored in a Word Wheel, so open one */ hMVGetTitle(View_ValidMV(lpV), buff); if ((lpV->hWordWheel = WordWheelOpen(buff, WWfile)) == 0) return(ERR_FAILED); /* load up list box with the keywords from this group */ numWords = (int)WordWheelLength(lpV->hWordWheel); for (i = 0; i < numWords; ++i) { /* get each keyword (they are already alphabatized) */ WordWheelLookup(lpV->hWordWheel, i, buff, sizeof(buff)); SendMessage(hList, LB_ADDSTRING, 0, (long)(LPSTR)buff); } return(0); } /**************************************************************************** ** FUNCTION: MV_IndexLookup ** ** PURPOSE: Create a list of topics containing the keyword ** ** COMMENTS: ** ****************************************************************************/ void MV_IndexLookup(HWND hMainWnd, HWND hList, LPVIEW lpV) { int selNumber; char buff[512]; selNumber = (int)SendMessage(hList, LB_GETCURSEL, 0, 0); SendMessage(hList, LB_GETTEXT, selNumber, (long)(LPSTR)buff); if (MV_Search(lpV, SEARCH_ALL, TRUE, 0, buff) == 0) { /* make sure that the Search Results display is visible */ if (!lpV->lpApp->hSearch) SendMessage(lpV->lpApp->hMain, WM_COMMAND, ID_VIEW_SEARCHRESULTS, 0); /* and show the results */ MV_ShowSearchResults(lpV); } } /**************************************************************************** ** FUNCTION: MV_IndexSelect ** ** PURPOSE: Set the list selection according to the character match. ** ** COMMENTS: ** ****************************************************************************/ int MV_IndexSelect(LPVIEW lpV, HWND hwnd, LPSTR szKey) { long index; /* this tells us the closest match in the keyword list */ index = WordWheelPrefix(lpV->hWordWheel, szKey); SendMessage(hwnd, LB_SETCURSEL, (WPARAM)index, 0); return(0); } /**************************************************************************** ** FUNCTION: MV_AddToHistory ** ** PURPOSE: Add the current topic to the history list ** ** COMMENTS: ** ** Location 0 always contains the oldest entry. If the array is ** ** full, move everything down and add the new entry to the end. ** ****************************************************************************/ void MV_AddToHistory(LPVIEW lpV, int source) { /* is the History buffer full? If so, make room for more */ if (iH == DIMENSION(History)-1) { GlobalFree(History[0].hName); /* shift everything down by one */ _fmemmove(History, History+1, sizeof(HISTORY) * (iH)); } else { /* otherwise bump the current History index */ ++iH; } History[iH].inUse = TRUE; MV_HistoryGetTopic(&History[iH], lpV); /* * update the Back pointer ... the strategy is to take it backward * each time Back is performed, but to reset it to the History * pointer when the SetTopic comes from any action the moves * forward (such as a hotspot jump). */ if (source != SET_FROM_BACK) iB = iH; /* if the history display is active, update it */ if (lpV->lpApp->hHist) MV_ShowHistory(lpV, GetDlgItem(lpV->lpApp->hHist, IDC_LIST1)); } /**************************************************************************** ** FUNCTION: MV_HistoryGetTopic ** ** PURPOSE: Put the topic address, etc. into the HISTORY structure ** ** COMMENTS: ** ****************************************************************************/ void MV_HistoryGetTopic(HISTORY * pH, LPVIEW lpV) { LPMV lpMV; INT subTopic; /* get a valid MV pointer */ lpMV = View_ValidMV(lpV); MVGetAddress(lpMV, &pH->va, &subTopic, &pH->scroll); if ((pH->hName = hMVGetName(lpMV)) != 0) pH->isName = TRUE; else { pH->hName = (HANDLE)lMVTopicNumber(View_ValidMV(lpV)); pH->isName = FALSE; } } /**************************************************************************** ** FUNCTION: MV_ShowHistory ** ** PURPOSE: Write the history strings into the display ** ** COMMENTS: ** ****************************************************************************/ void MV_ShowHistory(LPVIEW lpV, HWND hwnd) { LPSTR lp; char buff[20]; long topicNumber; int i; SendMessage(hwnd, LB_RESETCONTENT, 0, 0); if (iH < 0) return; for (i = iH; i >= 0; --i) { /* if there is a Topic Name, display it */ if (History[i].isName) lp = GlobalLock(History[i].hName); else { /* otherwise just create a topic number string */ topicNumber = lMVTopicNumber(View_ValidMV(lpV)); wsprintf(buff, "Topic #%d", (int)History[i].hName); lp = buff; } SendMessage(hwnd, LB_ADDSTRING, 0, (long)lp); } } /**************************************************************************** ** FUNCTION: MV_GoToFromHistory ** ** PURPOSE: Go to a new topic according to the history selection ** ** COMMENTS: ** ****************************************************************************/ void MV_GoToFromHistory(LPVIEW lpV, HWND hwnd) { int i; /* items are displayed in newest first, saved in history as oldest first */ i = (int)SendMessage(hwnd, LB_GETCOUNT, 0, 0) - 1 - (int)SendMessage(hwnd, LB_GETCURSEL, 0, 0); View_SetTopic(lpV, History[i].va, History[i].scroll, SET_FROM_OTHER); } /**************************************************************************** ** FUNCTION: MV_CleanupHistory ** ** PURPOSE: Free all of the History name strings ** ** COMMENTS: ** ****************************************************************************/ void MV_CleanupHistory() { int i; for (i = 0; i < DIMENSION(History); ++i) { if (History[i].inUse && History[i].hName) GlobalFree(History[i].hName); } _fmemset(History, 0, sizeof(History)); iH = 0; } /**************************************************************************** ** FUNCTION: MV_Back ** ** PURPOSE: Go back one in the history list. ** ** COMMENTS: ** ** See View_GoToFromHistory for comments on handling the back ** ** index. ** ****************************************************************************/ void MV_Back(LPVIEW lpV) { if (iB == -1) return; /* dec iB first ... the TopicEntry code needs to know the new value */ --iB; View_SetTopic(lpV, History[iB+1].va, History[iB+1].scroll, SET_FROM_BACK); /* if the list is full, everything moved down one to add the new entry */ if (iH == DIMENSION(History)-1) --iB; } /**************************************************************************** ** FUNCTION: MV_AddBookmark ** ** PURPOSE: Add an entry to the Bookmark menu. ** ** COMMENTS: ** ** This version allows up to 10. ** ****************************************************************************/ int MV_AddBookmark(HWND hwnd, LPVIEW lpV, LPSTR szName) { HMENU hMenu; if (iBM == DIMENSION(Bookmarks)-1) return(FALSE); /* get the handle of the Bookmark subMenu */ hMenu = GetMenu(hwnd); hMenu = GetSubMenu(hMenu, 2); /* Add the seperator if this is the first one */ if (++iBM == 0) AppendMenu(hMenu, MF_SEPARATOR, 0, 0); /* now add the new menu item */ AppendMenu(hMenu, MF_STRING|MF_ENABLED, ID_BOOKMARKBASE+iBM, szName); /* save the current topic address */ MV_HistoryGetTopic(&Bookmarks[iBM], lpV); return(TRUE); } /**************************************************************************** ** FUNCTION: MV_ShowBookmarks ** ** PURPOSE: Write the history strings into the display ** ** COMMENTS: ** ****************************************************************************/ void MV_ShowBookmarks(HWND hwnd, HWND hMainWnd) { int i; HMENU hMenu; char buff[512]; /* get the handle of the Bookmark subMenu */ hMenu = GetMenu(hMainWnd); hMenu = GetSubMenu(hMenu, 2); SendMessage(hwnd, LB_RESETCONTENT, 0, 0); if (iBM == -1) return; /* for each Bookmark, write the string into the list box */ for (i = 0; i <= iBM; ++i) { /* there is the "Define" and "SEPERATOR" first, so +2 */ GetMenuString(hMenu, 2+i, buff, sizeof(buff), MF_BYPOSITION); SendMessage(hwnd, LB_ADDSTRING, 0, (long)(LPSTR)buff); } } /**************************************************************************** ** FUNCTION: MV_GoToFromBookmark ** ** PURPOSE: Go to a new topic according to the bookmark selection ** ** COMMENTS: ** ****************************************************************************/ void MV_GoToFromBookmark(LPVIEW lpV, int bm) { bm -= ID_BOOKMARKBASE; View_SetTopic(lpV, Bookmarks[bm].va, Bookmarks[bm].scroll, SET_FROM_OTHER); } /**************************************************************************** ** FUNCTION: MV_DelBookmark ** ** PURPOSE: Delete a bookmark. ** ** COMMENTS: ** ****************************************************************************/ int MV_DelBookmark(HWND hwnd, HWND hMainWnd) { HMENU hMenu; int i; char buff[512]; /* delete the string from the list box */ i = (int)SendMessage(hwnd, LB_GETCURSEL, 0, 0); SendMessage(hwnd, LB_DELETESTRING, i, 0); /* get the handle of the Bookmark subMenu */ hMenu = GetMenu(hMainWnd); hMenu = GetSubMenu(hMenu, 2); /* delete the string from the menu */ DeleteMenu(hMenu, i+2, MF_BYPOSITION); /* delete the entry from the Bookmarks array */ if (i < iBM) _fmemmove(Bookmarks+i, Bookmarks+i+1, (iBM-i) * sizeof(HISTORY)); --iBM; /* now run through the menu and reset the IDs to be a contiguous block */ for (i = 0; i <= iBM; ++i) { /* allow for "Define" and SEPARATOR */ GetMenuString(hMenu, i+2, buff, sizeof(buff), MF_BYPOSITION); ModifyMenu(hMenu, i+2, MF_BYPOSITION|MF_STRING, i+ID_BOOKMARKBASE, buff); } /* last bookmark delete also deletes separator */ if (iBM == -1) DeleteMenu(hMenu, 1, MF_BYPOSITION); return(TRUE); } /**************************************************************************** ** FUNCTION: MV_LoadGroups ** ** PURPOSE: Load the group information from the MVP file. ** ** COMMENTS: ** ** Parse the INI file format and find the [GROUP] information. ** ** The application is responsible for GROUP information ... there ** ** is no direct MediaView support. Here we choose to extract the ** ** MVP file from baggage (under another name) and use ** ** the mmioOpen/Read calls to extract GROUP information. ** ** An application might also hard code the group information. ** ****************************************************************************/ LPGROUPUI MV_LoadGroups(LPSTR szTitleName, LPSTR szSection, LPSTR szKey) { HFILE hf; GROUPUI Group; LPGROUPUI lpGroup = 0; char *p, *q, buff[512], buff2[512]; int err, num = 0; /* transform the base title name to the MVP file name */ if (szTitleName[1] == ':') p = szTitleName+2; else p = szTitleName; q = _fstrrchr(p, '\\'); if (q != 0) p = q+1; /* find the extension */ _fstrcpy(buff, p); _strupr(buff); p = _fstrstr(buff, ".M12"); /* XXXXX.M12 => XXXXX.MVX */ if (p) _fstrcpy(p, ".MVX"); else goto earlyExit; /* Open the MediaView internal File */ wsprintf(buff2, "%s+%s", szTitleName, buff); _fstrupr(buff); if ((hf = mmioOpen(buff2, 0, MMIO_READ)) == HFILE_ERROR) { ErrorMsg(buff, "File open failure:"); return(0); } /* find the section */ while ((err = MV_GetLineHf(hf, buff)) != 0) { p = buff; while (*p && isspace(*p)) ++p; if (*p == '[' && _fstrnicmp(p+1, szSection, strlen(szSection)) == 0) break; } /* the section doesn't exist */ if (!err) goto earlyExit; /* section found ... names and titles */ while (MV_GetLineHf(hf, buff)) { /* skip blank lines and comments */ p = buff; while (*p && isspace(*p)) { if (*p == ';') break; ++p; } if (*p == 0 || *p == ';') continue; /* into the next section? */ if (*p == '[') break; /* got the line, now parse out the group name and title */ if (MV_ParseGroupName(buff, &Group, szKey)) { if (num == 0) lpGroup = (LPVOID)GlobalAllocPtr(GHND, sizeof(GROUPUI)); else lpGroup = (LPVOID)GlobalReAllocPtr(lpGroup, (num+1) * sizeof(GROUPUI), GHND); lpGroup[num++] = Group; } } /* close the MVP file */ mmioClose(hf, 0); earlyExit: /* add one more NULL group to mark the end */ if (num == 0) lpGroup = (LPVOID)GlobalAllocPtr(GHND, sizeof(GROUPUI)); else lpGroup = (LPVOID)GlobalReAllocPtr(lpGroup, (num+1) * sizeof(GROUPUI), GHND); lpGroup[num].name = 0; lpGroup[num].title = 0; return(lpGroup); } /**************************************************************************** ** FUNCTION: MV_GetLineHf ** ** PURPOSE: Read a logical line out of the HF file. ** ** COMMENTS: ** ****************************************************************************/ int MV_GetLineHf(HFILE hf, LPSTR lp) { do { if (mmioRead(hf, lp, 1) != 1) return(FALSE); ++lp; } while (lp[-1] != 0x0a); *lp = 0; return(TRUE); } /**************************************************************************** ** FUNCTION: MV_ParseGroupName ** ** PURPOSE: Parse out the Group name and title ** ** COMMENTS: ** ** group = , "" ****************************************************************************/ int MV_ParseGroupName(LPSTR lp, LPGROUPUI lpG, LPSTR szKey) { char *p; #define SKIP while(*lp && isspace(*lp))++lp SKIP; if (_fstrnicmp(lp, szKey, strlen(szKey))) return(FALSE); lp += strlen(szKey); SKIP; if (*lp++ != '=') return(FALSE); SKIP; /* this is the group name */ p = lp; while (*p && !isspace(*p) && *p != ',') ++p; lpG->name = GlobalAllocPtr(GHND, p - lp + 1); _fstrncpy(lpG->name, lp, p - lp); lpG->name[p - lp] = 0; lp = p; SKIP; if (*lp++ != ',') return(FALSE); SKIP; /* this is the title */ if (*lp++ != '"') return(FALSE); p = lp; while (*p && *p != '"') ++p; if (*p != '"') return(FALSE); lpG->title = GlobalAllocPtr(GHND, p - lp + 1); _fstrncpy(lpG->title, lp, p - lp); lpG->title[p - lp] = 0; return(TRUE); } /**************************************************************************** ** FUNCTION: MV_FreeGroups ** ** PURPOSE: Get the title's group list ** ** COMMENTS: ** ****************************************************************************/ LPGROUPUI MV_FreeGroups(LPGROUPUI lpGroup) { LPGROUPUI lpG = lpGroup; while (lpG && lpG->title) { GlobalFreePtr(lpG->title); GlobalFreePtr(lpG->name); ++lpG; } GlobalFreePtr(lpGroup); return(0); } /**************************************************************************** ** FUNCTION: MV_ShowMarkList ** ** PURPOSE: Load the list box with the current mark list ** ** COMMENTS: ** ****************************************************************************/ int MV_ShowMarkList(HWND hWnd) { int i; SendMessage(hWnd, LB_RESETCONTENT, 0, 0); for (i = 0; i <= iPM; ++i) { SendMessage(hWnd, LB_ADDSTRING, 0, (long)(LPSTR)PrintMarks[i].lpName); } return(0); } /**************************************************************************** ** FUNCTION: MV_AddPrintMark ** ** PURPOSE: Add a new print mark. ** ** COMMENTS: ** ****************************************************************************/ int MV_AddPrintMark(LPVIEW lpV, HWND hMarks, HWND hGroups, int markType) { LPMV lpMV = View_ValidMV(lpV); INT subTopic; int selNumber; long scroll; long topicNumber; HANDLE hName; LPSTR lp; char buff[512]; /* allocate a new PrintMark */ ++iPM; if (PrintMarks == 0) PrintMarks = (LPVOID)GlobalAllocPtr(GHND, sizeof(PRINTMARK)); else PrintMarks = (LPVOID)GlobalReAllocPtr(PrintMarks, (iPM+1) * sizeof(PRINTMARK), GHND); PrintMarks[iPM].type = markType; switch (markType) { case IDC_TOPIC: /* save away the current address information */ MVGetAddress(lpMV, &PrintMarks[iPM].va, &subTopic, &scroll); hName = hMVGetName(lpMV); if (hName) { /* build a string with the topic name */ lp = GlobalLock(hName); /* 8 == "Topic: " + \0 */ PrintMarks[iPM].lpName = GlobalAllocPtr(GHND, strlen(lp) + 8); wsprintf(PrintMarks[iPM].lpName, "Topic: %s", lp); GlobalFree(hName); } else { /* otherwise just create a topic number string */ topicNumber = lMVTopicNumber(lpMV); PrintMarks[iPM].lpName = GlobalAllocPtr(GHND, 32); wsprintf(PrintMarks[iPM].lpName, "Topic #%d", topicNumber); } break; case IDC_GROUPS: selNumber = (int)SendMessage(hGroups, LB_GETCURSEL, 0, 0); SendMessage(hGroups, LB_GETTEXT, selNumber, (long)(LPSTR)buff); /* 8 == "Group: " + \0 */ PrintMarks[iPM].lpName = GlobalAllocPtr(GHND, strlen(buff) + 8); wsprintf(PrintMarks[iPM].lpName, "Group: %s", buff); break; case IDC_ALL: break; } SendMessage(hMarks, LB_ADDSTRING, 0, (long)(LPSTR)PrintMarks[iPM].lpName); return(0); } /**************************************************************************** ** FUNCTION: MV_DelPrintMark ** ** PURPOSE: Delete a print mark ** ** COMMENTS: ** ****************************************************************************/ int MV_DelPrintMark(HWND hMarks) { int selNumber; /* which one to delete? */ selNumber = (int)SendMessage(hMarks, LB_GETCURSEL, 0, 0); /* take it out of the PrintMark list. No need to move if the * last one is being deleted. */ GlobalFreePtr(PrintMarks[selNumber].lpName); if (selNumber < iPM) _fmemmove(PrintMarks + selNumber, PrintMarks + selNumber + 1, sizeof(PRINTMARK) * (iPM - selNumber)); --iPM; /* and refresh the display */ MV_ShowMarkList(hMarks); return(0); } /**************************************************************************** ** FUNCTION: MV_FreePrintMarks ** ** PURPOSE: Free up all the allocated info about print marks ** ** COMMENTS: ** ****************************************************************************/ int MV_FreePrintMarks() { while (iPM != -1) { GlobalFreePtr(PrintMarks[iPM].lpName); --iPM; } if (PrintMarks) GlobalFreePtr(PrintMarks); PrintMarks = 0; return(0); } /**************************************************************************** ** FUNCTION: MV_PrintAll ** ** PURPOSE: Print all topics ** ** COMMENTS: ** ****************************************************************************/ int MV_PrintAll(LPVIEW lpV, HDC hdc) { long i, numTopics; QVA lpVA; /* create a list of VAs of all topics in the title */ numTopics = TitleGetInfo(lpV->hTitle, TTLINF_NUMTOPICS, 0, 0); lpVA = (LPVOID)GlobalAllocPtr(GHND, numTopics * sizeof(VA)); for (i = 0; i < numTopics; ++i) lpVA[i] = vaConvertTopicNumber(lpV->hTitle, i); /* print the list */ MV_PrintTopicList(lpV, hdc, lpVA, numTopics); GlobalFreePtr(lpVA); return(0); } /**************************************************************************** ** FUNCTION: MV_PrintMarkList ** ** PURPOSE: Print all the marked topics ** ** COMMENTS: ** ****************************************************************************/ int MV_PrintMarkList(LPVIEW lpV, HDC hdc) { int num; QVA lpVA = MV_CreateTopicList(lpV, &num); MV_PrintTopicList(lpV, hdc, lpVA, num); GlobalFreePtr(lpVA); return(0); } /**************************************************************************** ** FUNCTION: MV_CreateTopicList ** ** PURPOSE: Create a list of VA structures from the PrintMark list ** ** COMMENTS: ** ****************************************************************************/ QVA MV_CreateTopicList(LPVIEW lpV, LPINT lpNum) { int i, j; HTLIST ht; QVA lpVA; LPGROUPUI lpG; long topicNum, topicLength, ii; /* how many topics do we need? */ *lpNum = 0; for (i = 0; i <= iPM; ++i) switch (PrintMarks[i].type) { case IDC_TOPIC: ++*lpNum; break; case IDC_GROUPS: /* get the Group Name (the title is in the PrintMarks list) */ for (lpG = lpV->lpGroups; lpG && lpG->title; ++lpG) { /* 7 == "Group: " */ if (_fstricmp(lpG->title, 7+PrintMarks[i].lpName) == 0) break; } /* get number of topics in the group */ ht = TopicListLoad(lpV->hTitle, lpG->name); topicLength = TopicListLength(ht); *lpNum += (int)topicLength; break; } /* allocate space for the list */ lpVA = (LPVOID)GlobalAllocPtr(GHND, *lpNum * sizeof(VA)); /* and copy each VA to it */ for (i = 0, j = 0; i <= iPM; ++i ) switch (PrintMarks[i].type) { case IDC_TOPIC: lpVA[j++] = PrintMarks[i].va; break; case IDC_GROUPS: /* step through and get the VA for each topic in the group */ for (ii = 0; ii < topicLength; ++ii) { topicNum = TopicListLookup(ht, ii); lpVA[j++] = vaConvertTopicNumber(lpV->hTitle, topicNum); } TopicListDestroy(ht); break; } return(lpVA); } /**************************************************************************** ** FUNCTION: MV_PrintTopic ** ** PURPOSE: Print the current topic ** ** COMMENTS: ** ****************************************************************************/ int MV_PrintTopic(LPVIEW lpV, HDC hdc) { VA va; LPMV lpMV = View_ValidMV(lpV); INT subTopic; long scroll; MVGetAddress(lpMV, &va, &subTopic, &scroll); MV_PrintTopicList(lpV, hdc, &va, 1); return(0); } /**************************************************************************** ** FUNCTION: MV_PrintTopicList ** ** PURPOSE: Print topics (both subTopics) from a list. ** ** COMMENTS: ** ****************************************************************************/ int MV_PrintTopicList(LPVIEW lpV, HDC hdc, QVA lpTopics, long numTopics) { LPMV lpMV; ERR err; RECT rctPrint, rctPage; POINT pt; HCURSOR hCursor; DOCINFO di; HANDLE hName; VA currentVA; INT subTopic; int i; long scroll; /* remember the current topic */ lpMV = View_ValidMV(lpV); MVGetAddress(lpMV, ¤tVA, &subTopic, &scroll); /* this might take a while */ hCursor = SetCursor(LoadCursor(0, IDC_WAIT)); err = wERRS_NONE; /* Get the physical page size for this printer. */ if( Escape(hdc, GETPHYSPAGESIZE, 0, NULL, (LPSTR)&pt) <= 0 ) { err = wERRS_BADPRINT; goto PrintError; } /* start the logical print document */ hName = hMVGetName(lpMV); di.cbSize = sizeof(DOCINFO); di.lpszOutput = NULL; di.lpszDocName = hName ? (LPSTR)GlobalLock(hName) : ""; /* Provide a StartPage and EndPage for each topic. MediaView will * call back through plfnPageCallBack to ask for Start/End Page * for any interim page breaks inside a topic. */ if (StartDoc(hdc, (DOCINFO FAR *)&di) == SP_ERROR) { err = wERRS_BADPRINT; goto PrintError; } /* for each topic in the topic List */ for (i = 0; i < numTopics; ++i) { /* start the first page ... add header here if you want one */ if (StartPage(hdc) <= 0) goto PrintError; /* Set up the printing rectangle for each page (Topic). * With MM_TEXT mode, the following gives 1 inch margins. */ rctPage.left = GetDeviceCaps(hdc, LOGPIXELSX); rctPage.right = pt.x - rctPage.left; rctPage.top = GetDeviceCaps(hdc, LOGPIXELSY); rctPage.bottom = pt.y - rctPage.top; CopyRect((LPRECT)&rctPrint, (LPRECT)&rctPage); /* get the layout for the printer */ View_SetTopic(lpV, lpTopics[i], 0, SET_FROM_PRINT); /* if there is a non-scrolling region, print it */ if (fMVHasNSR(GetPaneMV(lpV->hNSR))) { lpMV = GetPaneMV(lpV->hNSR); if (!fMVPrintMedia(lpMV, hdc, rctPage, &rctPrint, MV_PageCallBack, 0L, &err)) goto PrintError; } /* rctPrint describes the remaining space on the page for the scrolling region */ if (fMVHasSR(GetPaneMV(lpV->hSR))) { lpMV = GetPaneMV(lpV->hSR); if (!fMVPrintMedia(lpMV, hdc, rctPage, &rctPrint, MV_PageCallBack, 0L, &err)) goto PrintError; } /* close the final page (of the topic) */ if (EndPage(hdc) < 0) goto PrintError; } /* close the logical print document */ EndDoc(hdc); err = 0; /* clean up and get out */ PrintError: if (err != 0) AbortDoc(hdc); if (hCursor) SetCursor(hCursor); DeleteDC(hdc); if (hName) GlobalFree(hName); /* restore the current topic */ View_SetTopic(lpV, currentVA, scroll, SET_FROM_PRINT); return err; } /**************************************************************************** ** FUNCTION: MV_PageCallBack ** ** PURPOSE: Print a topic (both subTopics). ** ** COMMENTS: ** ** This could be used to put headers or footers on each page ** ****************************************************************************/ BOOL _export far PASCAL MV_PageCallBack(LONG lNotUsed, HDC hdc, RECT rectPrinter, LPRECT lpPage, BOOL fStartPage) { /* MediaView tells when each interim EndPage or StartPage should occur */ if( fStartPage ) return (BOOL)(StartPage(hdc) > 0); else return (BOOL)(EndPage(hdc) >= 0); }