History accessory The history accessory conforms to the W3A specification for user functions. It exports three functions: [[initHistory]], [[doHistory]] and [[eventHistory]]. [[initHistory]] creates an ID. Since there is only one instance of each accessory (the browser should take care of that), the accessory doesn't need to know its own ID. In conformance to the recommended way for creating a unique ID, the [[initHistorytlist]] function allocates some space on the heap and returns the pointer as its ID. [[doHistory]] has three modes of operation: when it is called with code 2 ([[BACK]]), it opens the previous document; when it is called with code 3 ([[FORWARD]]), it opens the next document, if the user has gone back previously; with code 1, it opens a list with all the documents the user has been before. The dialog box is created if it doesn't exist. [[eventHistory]] is called by the browser whenever a W3A event occurs. The only event that is useful is event 1 ([[NEW_DOCUMENT]]). By keeping track of these events, the history accessory knows what the `current document'. TO DO: use `hierarchical history'? TO DO: display history as an HTML page in the main browser (?) Bert Bos , 28 Nov 1994 <<*>>= static char copyright[] = "Copyright NBBI, Den Haag, 1994"; #include #include #include #include #include #include #include #include #include #include #include #include #if 0 #include "open.xpm" #include "open1.xpm" #include "back.xpm" #include "back1.xpm" #include "forward.xpm" #include "forward1.xpm" #endif #include "back.xpm" #include "back1.xpm" #include "forward.xpm" #include "forward1.xpm" #include "history.xpm" #include "open.xpm" #include "open1.xpm" #define BACK 1 /* Codes for doHistory() */ #define FORWARD 2 #define OPEN_DIALOG 3 #define INCR 256 #define max(a, b) ((a) > (b) ? (a) : (b)) typedef struct {Strip title, url;} *ListType; static W3ADocumentInfo *curdoc = NULL; static Widget dialog = NULL, listw = NULL; static ListType list = NULL; static int nritems = 0; static int curnr = -1; /* Current pos in history */ @ The title and URL of the history items are kept in two different places: the accessory's own in-memory list, and in the List widget. The two are kept synchronized. <<*>>= /* add_current -- append URL of current document to the history file */ static Bool add_current(void) { XmString ms; time_t tm; FILE *f; Strip url; int i, j; assert(curdoc != NULL); url = str2strip(curdoc->url); /* First see if the document has been visited earlier */ for (i = 0; i < nritems; i++) if (list[i].url == url) break; if (i < nritems) { /* Already seen this URL */ if (curdoc->title) { /* Change title */ list[i].title = str2strip(curdoc->title); ms = XmStringCreateLocalized(curdoc->title); XmListReplaceItemsPos(listw, &ms, 1, i + 1); } curnr = i; XmListSetBottomPos(listw, i + 1); } else { /* New document */ /* Add it to the in-memory list */ if (nritems % INCR == 0) renewarray(list, ((nritems + INCR)/INCR) * INCR); list[nritems].title = str2strip(curdoc->title ? curdoc->title : curdoc->url); list[nritems].url = url; curnr = nritems; nritems++; /* Add it to the list widget in the dialog */ ms = XmStringCreateLocalized(curdoc->title?curdoc->title:curdoc->url); XmListAddItem(listw, ms, 0); XmStringFree(ms); XmListSetBottomPos(listw, 0); } return TRUE; } /* goto_document -- open document `item' */ static void goto_document(int item) { W3ADocumentInfo *doc; assert(listw); curnr = item; doc = new_doc(); doc->url = newstring(strip2str(list[item].url)); doc->title = newstring(strip2str(list[item].title)); (void) W3Aprocess(doc, GET_METHOD, NULL, 0); dispose_doc(doc); XmListSetBottomPos(listw, item + 1); } /* open_document_cb -- handle a double click on a list item */ static void open_document_cb(Widget w, XtPointer client_data, XtPointer call_data) { int *selected, nrselected; #if 1 XtPopdown(dialog); #endif if (XmListGetSelectedPos(listw, &selected, &nrselected)) { goto_document(selected[0] - 1); XtFree((char *) selected); } } /* back -- open previous document */ static Bool go_back() { if (curnr <= 0) return FALSE; goto_document(curnr - 1); return TRUE; } /* forward -- open next document */ static Bool go_forward() { if (curnr >= nritems - 1) return FALSE; goto_document(curnr + 1); return TRUE; } /* cancel_cb -- handle a click on the `cancel' button */ static void cancel_cb(Widget w, XtPointer client_data, XtPointer call_data) { #if 1 XtPopdown(dialog); #endif } /* back_cb -- handle a click on the `back' button */ static void back_cb(Widget w, XtPointer client_data, XtPointer call_data) { #if 1 XtPopdown(dialog); #endif go_back(); } /* forward_cb -- handle a click on the `forward' button */ static void forward_cb(Widget w, XtPointer client_data, XtPointer call_data) { #if 1 XtPopdown(dialog); #endif go_forward(); } /* create_dialog -- create the dialog box, install callbacks */ static Bool create_dialog(void) { Widget formw, gotow, cancelw, backw, forwardw; Atom WM_Delete_Window; #if 0 dialog = XtVaCreatePopupShell("history", xmDialogShellWidgetClass, W3Atoplevel(), XtNtitle, "History", NULL); #else dialog = XtVaCreatePopupShell("history", transientShellWidgetClass, W3Atoplevel(), XtNtitle, "History", NULL); #endif formw = XtVaCreateManagedWidget ("form", xmFormWidgetClass, dialog, XmNmarginHeight, 10, XmNmarginWidth, 10, XmNhorizontalSpacing, 10, XmNverticalSpacing, 10, NULL); gotow = XmCreatePixmapPushButtonGadget (formw, "open", open, open1, NULL, NULL, 0); XtManageChild(gotow); backw = XmCreatePixmapPushButtonGadget (formw, "back", back, back1, NULL, NULL, 0); XtManageChild(backw); forwardw = XmCreatePixmapPushButtonGadget (formw, "forward", forward, forward1, NULL, NULL, 0); XtManageChild(forwardw); #if 0 cancelw = XtVaCreateManagedWidget ("cancel", xmPushButtonGadgetClass, formw, NULL); #else cancelw = XmCreateStandardPushButtonGadget (formw, "cancel", XmPUSHBUTTON_CANCEL, NULL, 0); XtManageChild(cancelw); #endif listw = XmCreateScrolledList(formw, "list", NULL, 0); XtManageChild(listw); XtAddCallback(gotow, XmNactivateCallback, open_document_cb, NULL); XtAddCallback(backw, XmNactivateCallback, back_cb, NULL); XtAddCallback(forwardw, XmNactivateCallback, forward_cb, NULL); XtAddCallback(cancelw, XmNactivateCallback, cancel_cb, NULL); XtAddCallback(listw, XmNdefaultActionCallback, open_document_cb, NULL); XtVaSetValues (XtParent(listw), XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, gotow, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtVaSetValues (gotow, XmNtopAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); XtVaSetValues (backw, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftWidget, gotow, XmNleftOffset, 0, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, gotow, XmNrightAttachment, XmATTACH_FORM, NULL); XtVaSetValues (forwardw, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftWidget, backw, XmNleftOffset, 0, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, backw, XmNrightAttachment, XmATTACH_FORM, NULL); XtVaSetValues (cancelw, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftWidget, gotow, XmNleftOffset, 0, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); /* Intercept the WM_DELETE_WINDOW protocol */ XtVaSetValues(dialog, XmNdeleteResponse, XmUNMAP, NULL); XtVaSetValues(formw, XmNcancelButton, cancelw, NULL); return TRUE; } /* open_dialog -- open a dialog and let users select a history entry */ static Bool open_dialog(int mid_x, int mid_y) { int width, height; assert(dialog != NULL); XtVaGetValues(dialog, XmNwidth, &width, XmNheight, &height, NULL); XtVaSetValues(dialog, XmNx, max(0, mid_x - width/2), XmNy, max(0, mid_y - height/2), NULL); #if 1 XtPopup(dialog, XtGrabNonexclusive); #else XtManageChild(dialog); #endif return TRUE; } EXPORT Bool initHistory(long id, char ***labels, ThreeIcons *icons_data[], int *nrlabels) { static char *labs[] = { "Back", "Forward", "View history"}; static ThreeIcons data[] = { {back, back1, NULL}, {forward, forward1, NULL}, {history, NULL, NULL}}; char s[200]; if (! create_dialog()) { sprintf(s, "Couldn't create history dialog"); XtAppWarning(XtWidgetToApplicationContext(W3Atoplevel()), s); return -1; } *labels = labs; *icons_data = data; *nrlabels = XtNumber(labs); return True; } EXPORT Bool doHistory(int code, int mid_x, int mid_y) { switch (code) { case OPEN_DIALOG: return open_dialog(mid_x, mid_y); case BACK: return go_back(); case FORWARD: return go_forward(); default: assert(! "Cannot happen"); } } EXPORT void eventHistory(long sourceid, long eventtype, void *params) { if (eventtype == NEW_DOCUMENT) { if (curdoc) dispose_doc(curdoc); curdoc = new_doc(); copy_doc(curdoc, *((W3ADocumentInfo *) params)); add_current(); } }