/* $Xorg: PrintShell.c,v 1.1 2003/07/11 19:46:06 gisburn Exp $ */ /****************************************************************************** ****************************************************************************** ** ** (c) Copyright 2003 Danny Backx ** (c) Copyright 2003-2004 Roland Mainz ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal ** in the Software without restriction, including without limitation the rights ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ** copies of the Software, and to permit persons to whom the Software is ** furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in ** all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ** Except as contained in this notice, the names of the copyright holders shall ** not be used in advertising or otherwise to promote the sale, use or other ** dealings in this Software without prior written authorization from said ** copyright holders. ** ****************************************************************************** *****************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "Print.h" #include "PrintSP.h" /* Local prototypes */ static void class_initialize(void); static void class_part_initialize(WidgetClass w_class); static void initialize(Widget request, Widget new_w, ArgList args, Cardinal *num_args); static void destroy(Widget w); static Boolean set_values(Widget current, Widget request, Widget new_w, ArgList args, Cardinal *num_args); static void XawPrintNotify(Widget w, XtPointer client, XEvent *evp, Boolean *cont); static void XawAttributesNotify(Widget w, XtPointer client, XEvent *evp, Boolean *cont); static void XawUpdateLayout(Widget w); static void XawUpdateResources(Widget w, XPContext pcontext); #define Offset(field) XtOffsetOf(XawPrintShellRec, print.field) #ifdef XAWDEBUG #define DEBUGOUT(x) XawDebug x ; static void XawDebug(const char *fn, Widget w, const char *fmt, ...) { va_list ap; if (w) { fprintf(stderr, "%s %s: ", w->core.widget_class->core_class.class_name, XtName(w)); } else { fprintf(stderr, "(null widget): "); } va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } #else #define DEBUGOUT(x) #endif /* XAWDEBUG */ /* Resources for the PrintShell class */ static XtResource resources[] = { { XawNstartJobCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), Offset(start_job_callback), XtRImmediate, (XtPointer)NULL }, { XawNendJobCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), Offset(end_job_callback), XtRImmediate, (XtPointer)NULL }, { XawNdocSetupCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), Offset(doc_setup_callback), XtRImmediate, (XtPointer)NULL }, { XawNpageSetupCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), Offset(page_setup_callback), XtRImmediate, (XtPointer)NULL }, { XawNlayoutMode, XawCLayoutMode, XtREnum, sizeof(XtEnum), Offset(layoutmode), XtRImmediate, (XtPointer)XawPrintLAYOUTMODE_PAGESIZE }, { XawNminX, XawCMinX, XtRDimension, sizeof(Dimension), Offset(min_x), XtRImmediate, (XtPointer)NULL /* dynamic */ }, { XawNminY, XawCMinY, XtRDimension, sizeof(Dimension), Offset(min_y), XtRImmediate, (XtPointer)NULL /* dynamic */ }, { XawNmaxX, XawCMaxX, XtRDimension, sizeof(Dimension), Offset(max_x), XtRImmediate, (XtPointer)NULL /* dynamic */ }, { XawNmaxY, XawCMaxY, XtRDimension, sizeof(Dimension), Offset(max_y), XtRImmediate, (XtPointer)NULL /* dynamic */ }, { XawNcurrDocNumInJob, XawCCurrDocNumInJob, XtRInt, sizeof(unsigned int), Offset(curr_doc_num_in_job), XtRImmediate, (XtPointer)NULL /* dynamic */ }, { XawNcurrPageNumInDoc, XawCCurrPageNumInDoc, XtRInt, sizeof(unsigned int), Offset(curr_page_num_in_doc), XtRImmediate, (XtPointer)NULL /* dynamic */ }, { XawNcurrPageNumInJob, XawCCurrPageNumInJob, XtRInt, sizeof(unsigned int), Offset(curr_page_num_in_job), XtRImmediate, (XtPointer)NULL /* dynamic */ }, { XawNdefaultPixmapResolution, XawCDefaultPixmapResolution, XtRShort, sizeof(unsigned short), Offset(default_pixmap_resolution), XtRImmediate, (XtPointer)0 }, }; static XtActionsRec actions[] = { { NULL, NULL } }; XawPrintShellClassRec xawPrintShellClassRec = { /* Core class part */ { /* superclass */ (WidgetClass) &applicationShellClassRec, /* class_name */ "XawPrintShell", /* widget_size */ sizeof(XawPrintShellRec), /* class_initialize */ class_initialize, /* class_part_initialize */ class_part_initialize, /* class_inited */ False, /* initialize */ initialize, /* initialize_hook */ NULL, /* realize */ XtInheritRealize, /* actions */ actions, /* num_actions */ XtNumber(actions), /* resources */ resources, /* num_resources */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ False, /* compress_exposure */ XtExposeCompressSeries, /* compress_enterleave */ False, /* visible_interest */ False, /* destroy */ destroy, /* resize */ XtInheritResize, /* expose */ XtInheritExpose, /* set_values */ set_values, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ NULL, /* version */ XtVersion, /* callback offsets */ NULL, /* tm_table */ XtInheritTranslations, /* query_geometry */ XtInheritQueryGeometry, /* display_accelerator */ NULL, /* extension */ NULL /* (XtPointer)&_XawPrintShellCoreClassExtRec */ }, /* Composite class part */ { /* geometry manager */ XtInheritGeometryManager, /* change_managed */ XtInheritChangeManaged, /* insert_child */ XtInheritInsertChild, /* delete_child */ XtInheritDeleteChild, /* extension */ NULL, }, /* Shell class part */ { /* extension */ NULL, }, /* WM Shell class part */ { /* extension */ NULL, }, /* VendorShell class part */ { /* extension */ NULL, }, /* TopLevelShell class part */ { /* extension */ NULL, }, /* ApplicationShell class part */ { /* extension */ NULL, }, { /* ?? */ NULL, }, }; WidgetClass xawPrintShellWidgetClass = (WidgetClass)&xawPrintShellClassRec; static void class_initialize(void) { } static void class_part_initialize(WidgetClass widget_class) { } /* * This is a static table to keep the link between widgets and XPContexts. * Yeah - this is probably not a very bright idea. Maybe it should also * contain the Display. */ typedef struct { Widget w; XPContext c; } WidgetContext; static WidgetContext *w_ctxt = NULL; static int wc_nfields = 0; static void XawStoreWidgetContext(Widget w, XPContext c) { wc_nfields++; w_ctxt = (WidgetContext *)XtRealloc((XtPointer)w_ctxt, sizeof(WidgetContext) * wc_nfields); w_ctxt[wc_nfields-1].w = w; w_ctxt[wc_nfields-1].c = c; } /* FIXME: This is not threadsafe... */ static Widget XawPrintContextToWidget(XPContext c) { int i; for( i = 0 ; i < wc_nfields ; i++ ) { if( w_ctxt[i].c == c ) { return w_ctxt[i].w; } } return NULL; } /* FIXME: This is not threadsafe... */ static XPContext XawPrintWidgetToContext(Widget w) { int i; for( i = 0 ; i < wc_nfields ; i++ ) { if (w_ctxt[i].w == w) { return w_ctxt[i].c; } } return (XPContext)None; } /* FIXME: This is not threadsafe... */ static void XawPrintDeleteWidgetContext(Widget w) { int i; for( i = 0 ; i < wc_nfields ; i++ ) { if( w_ctxt[i].w == w ) { w_ctxt[i].w = NULL; w_ctxt[i].c = None; } } } static void SelectNotify(Widget w, int *e, XtPointer *s, int n, XtPointer client) { XPContext c = XpGetContext(XtDisplay(w)); if (!c) { XtAppWarning(XtWidgetToApplicationContext(w), "XawPrintShell: SelectNotify: no print context\n"); return; } XpSelectInput(XtDisplay(w), c, XPPrintMask|XPAttributeMask); } static Boolean DispatchEvent(XEvent *evp) { XPPrintEvent *e = (XPPrintEvent*)evp; Widget w = XawPrintContextToWidget(e->context); /* Make sure this event is really for this window... */ if (XFilterEvent(evp, XtWindow(w))) { DEBUGOUT((__FILE__, w, "XawPrintShell-DispatchEvent *** filter XFilterEvent() matched.\n")); return True; } /* Only for debugging */ #ifdef XAWDEBUG { int error_base, event_base; if (!XpQueryExtension(XtDisplay(w), &event_base, &error_base)) { return False; } if (e->type == event_base + XPPrintNotify) { switch (e->detail) { case XPStartJobNotify: DEBUGOUT((__FILE__, w, "XawPrintShell-DispatchEvent XPStartJobNotify\n")); break; case XPEndJobNotify: DEBUGOUT((__FILE__, w, "XawPrintShell-DispatchEvent XPEndJobNotify\n")); break; case XPStartDocNotify: DEBUGOUT((__FILE__, w, "XawPrintShell-DispatchEvent XPStartDocNotify\n")); break; case XPStartPageNotify: DEBUGOUT((__FILE__, w, "XawPrintShell-DispatchEvent XPStartPageNotify\n")); break; case XPEndPageNotify: DEBUGOUT((__FILE__, w, "XawPrintShell-DispatchEvent XPEndPageNotify\n")); break; case XPEndDocNotify: DEBUGOUT((__FILE__, w, "XawPrintShell-DispatchEvent XPEndDocNotify\n")); break; default: DEBUGOUT((__FILE__, w, "XawPrintShell DispatchEvent\n")); } } } #endif /* XAWDEBUG */ return XtDispatchEventToWidget(w, evp); } static void initialize(Widget request, Widget new_w, ArgList args, Cardinal *num_args) { int error_base, event_base; XPContext pcontext; DEBUGOUT((__FILE__, new_w, "XawPrintShell Initialize\n")); if (!XpQueryExtension(XtDisplay(new_w), &event_base, &error_base)) { DEBUGOUT((__FILE__, new_w, "XawPrintShell initialize: failed!!\n")); XtAppWarning(XtWidgetToApplicationContext(new_w), "XawPrintShell: initialize: XpQueryExtension() failed. BAD.\n"); return; } DEBUGOUT((__FILE__, new_w, "XawPrintShell Initialize event_base %d error_base %d\n", event_base, error_base)); pcontext = XpGetContext(XtDisplay(new_w)); if( pcontext == None ) { XtAppWarning(XtWidgetToApplicationContext(new_w), "XawPrintShell: initialize: No print content. BAD.\n"); return; } /* Make sure that the Xt machinery is really using the right screen (assertion) */ if( XpGetScreenOfContext(XtDisplay(new_w), pcontext) != XtScreen(new_w) ) { XtAppWarning(XtWidgetToApplicationContext(new_w), "XawPrintShell: initialize: Widget's screen != print screen. BAD.\n"); return; } XawStoreWidgetContext(new_w, pcontext); XtInsertEventTypeHandler(new_w, event_base + XPPrintNotify, (XtPointer)XPPrintMask, XawPrintNotify, NULL, XtListTail); XtInsertEventTypeHandler(new_w, event_base + XPAttributeNotify, (XtPointer)XPAttributeMask, XawAttributesNotify, NULL, XtListTail); XtRegisterExtensionSelector(XtDisplay(new_w), event_base + XPPrintNotify, event_base + XPAttributeNotify, SelectNotify, NULL); XtSetEventDispatcher(XtDisplay(new_w), event_base + XPPrintNotify, DispatchEvent); XtSetEventDispatcher(XtDisplay(new_w), event_base + XPAttributeNotify, DispatchEvent); PS_LastPageInDoc(new_w) = False; PS_LastPageInJob(new_w) = False; XawUpdateResources(new_w, pcontext); XawUpdateLayout(new_w); DEBUGOUT((__FILE__, new_w, "XawPrintShell Initialize x %d y %d wid %d ht %d\n", new_w->core.x, new_w->core.y, new_w->core.width, new_w->core.height)); } static void destroy(Widget w) { DEBUGOUT((__FILE__, w, "XawPrintShell Destroy\n")); XawPrintDeleteWidgetContext(w); } static Boolean set_values(Widget current, Widget request, Widget new_w, ArgList args, Cardinal *num_args) { DEBUGOUT((__FILE__, new_w, "XawPrintShell SetValues\n")); return True; } void XawPrintRedisplayWidget(Widget w) { XExposeEvent xev; Region region; xev.type = Expose; xev.serial = XLastKnownRequestProcessed(XtDisplay(w)); xev.send_event = False; xev.display = XtDisplay(w); xev.window = XtWindowOfObject(w); xev.x = 0; xev.y = 0; xev.width = w->core.width; xev.height = w->core.height; xev.count = 0; region = XCreateRegion(); if (!region) return; XtAddExposureToRegion((XEvent*)&xev, region); if (w->core.widget_class->core_class.expose) (*(w->core.widget_class->core_class.expose))(w, (XEvent *)&xev, region); XDestroyRegion(region); } /* Returns whether the widget passed in is a print shell or "print shell"-like * widget (e.g. print preview). * Note that this will return |True| for more classes than |XawPrintShell| in * the future (like for |XmPrintShell| etc.) */ Boolean XawIsPrintShell(Widget w) { return XtIsSubclass(w, xawPrintShellWidgetClass); } static void XawPrintNotify(Widget w, XtPointer client, XEvent *evp, Boolean *cont) { XPPrintEvent *e = (XPPrintEvent *)evp; XawPrintShellCallbackStruct cbs; switch (e->detail) { case XPStartPageNotify: DEBUGOUT((__FILE__, w, "XPStartPageNotify\n")); /* Re do not have to call |XawPrintRedisplayWidget(w)| here since * Xprint triggers an expose event anyway */ DEBUGOUT((__FILE__, w, "XpEndPage\n")); XpEndPage(XtDisplay(w)); break; case XPEndPageNotify: DEBUGOUT((__FILE__, w, "XPEndPageNotify\n")); if (PS_LastPageInDoc(w) || PS_LastPageInJob(w)) { DEBUGOUT((__FILE__, w, "XpEndDoc\n")); XpEndDoc(XtDisplay(w)); } else { /* Increment page numbers... */ PS_CurrPageNumInDoc(w) += 1; PS_CurrPageNumInJob(w) += 1; /* ... do the page setup callback ... */ cbs.reason = XawCR_PAGE_SETUP; cbs.event = evp; cbs.detail = NULL; cbs.context = XawPrintWidgetToContext(w); cbs.last_page_in_doc = False; cbs.last_page_in_job = False; if (PS_PageSetupCallback(w)) XtCallCallbackList(w, PS_PageSetupCallback(w), &cbs); PS_LastPageInDoc(w) = cbs.last_page_in_doc; PS_LastPageInJob(w) = cbs.last_page_in_job; /* ... and start the new page */ DEBUGOUT((__FILE__, w, "XpStartPage\n")); XpStartPage(XtDisplay(w), XtWindow(w)); } break; case XPStartDocNotify: DEBUGOUT((__FILE__, w, "XPStartDocNotify\n")); cbs.reason = XawCR_PAGE_SETUP; cbs.event = evp; cbs.detail = NULL; cbs.context = XawPrintWidgetToContext(w); cbs.last_page_in_doc = False; cbs.last_page_in_job = False; if (PS_PageSetupCallback(w)) XtCallCallbackList(w, PS_PageSetupCallback(w), &cbs); PS_LastPageInDoc(w) = cbs.last_page_in_doc; PS_LastPageInJob(w) = cbs.last_page_in_job; DEBUGOUT((__FILE__, w, "XpStartPage\n")); XpStartPage(XtDisplay(w), XtWindow(w)); break; case XPEndDocNotify: DEBUGOUT((__FILE__, w, "XPEndDocNotify\n")); /* Start a new document (via XpStartDoc()) if we are not done with the job yet, * otherwise finish the job (via XpEndJob()) */ if (PS_LastPageInJob(w)) { DEBUGOUT((__FILE__, w, "XpEndJob\n")); XpEndJob(XtDisplay(w)); } else { PS_CurrDocNumInJob(w) += 1; PS_CurrPageNumInDoc(w) = 1; cbs.reason = XawCR_DOC_SETUP; cbs.event = evp; cbs.detail = NULL; cbs.context = XawPrintWidgetToContext(w); cbs.last_page_in_doc = False; cbs.last_page_in_job = False; if (PS_DocSetupCallback(w)) XtCallCallbackList(w, PS_DocSetupCallback(w), &cbs); PS_LastPageInDoc(w) = cbs.last_page_in_doc; PS_LastPageInJob(w) = cbs.last_page_in_job; DEBUGOUT((__FILE__, w, "XpStartDoc\n")); XpStartDoc(XtDisplay(w), XPDocNormal); } break; case XPStartJobNotify: DEBUGOUT((__FILE__, w, "XPStartJobNotify\n")); PS_LastPageInJob(w) = False; PS_LastPageInDoc(w) = False; PS_CurrDocNumInJob(w) = 1; PS_CurrPageNumInDoc(w) = 1; PS_CurrPageNumInJob(w) = 1; cbs.reason = XawCR_START_JOB; cbs.event = evp; cbs.detail = NULL; cbs.context = XawPrintWidgetToContext(w); cbs.last_page_in_doc = False; cbs.last_page_in_job = False; if (PS_StartJobCallback(w)) XtCallCallbackList(w, PS_StartJobCallback(w), &cbs); PS_LastPageInDoc(w) = cbs.last_page_in_doc; PS_LastPageInJob(w) = cbs.last_page_in_job; /* Start a document (which will trigger the first page in * |XPStartDocNotify| above) */ if (PS_LastPageInDoc(w) || PS_LastPageInJob(w)) { DEBUGOUT((__FILE__, w, "XpEndJob\n")); XpEndJob(XtDisplay(w)); } else { cbs.reason = XawCR_DOC_SETUP; cbs.event = evp; cbs.detail = NULL; cbs.context = XawPrintWidgetToContext(w); cbs.last_page_in_doc = False; cbs.last_page_in_job = False; if (PS_DocSetupCallback(w)) XtCallCallbackList(w, PS_DocSetupCallback(w), &cbs); PS_LastPageInDoc(w) = cbs.last_page_in_doc; PS_LastPageInJob(w) = cbs.last_page_in_job; DEBUGOUT((__FILE__, w, "XpStartDoc\n")); XpStartDoc(XtDisplay(w), XPDocNormal); } break; case XPEndJobNotify: DEBUGOUT((__FILE__, w, "XPEndJobNotify\n")); cbs.reason = XawCR_END_JOB; cbs.event = evp; cbs.detail = NULL; cbs.context = None; cbs.last_page_in_doc = True; cbs.last_page_in_job = True; if (PS_EndJobCallback(w)) XtCallCallbackList(w, PS_EndJobCallback(w), &cbs); break; default: DEBUGOUT((__FILE__, w, "XawPrintNotify(default)\n")); break; } } static void XawUpdateResources(Widget w, XPContext pcontext) { XawPrintShellWidget print_shell = (XawPrintShellWidget)w; String string_resolution; XRectangle drawable_paper_area; string_resolution = XpGetOneAttribute(XtDisplay(w), pcontext, XPDocAttr, "default-printer-resolution"); if (!string_resolution) { XtAppWarning(XtWidgetToApplicationContext(w), "XawPrintShell: XawUpdateResources: Could not get 'default-printer-resolution' XPDocAttr\n"); } print_shell->print.print_resolution = atol(string_resolution); XFree(string_resolution); if (print_shell->print.print_resolution == 0) { XtAppWarning(XtWidgetToApplicationContext(w), "XawPrintShell: XawUpdateResources: Resolution '0' invalid\n"); } /* Get the paper size... */ XpGetPageDimensions(XtDisplay(w), pcontext, &print_shell->print.page_width, &print_shell->print.page_height, &drawable_paper_area); /* ... and store it in the widget */ print_shell->print.min_x = drawable_paper_area.x; print_shell->print.min_y = drawable_paper_area.y; print_shell->print.max_x = drawable_paper_area.x + drawable_paper_area.width; print_shell->print.max_y = drawable_paper_area.y + drawable_paper_area.height; } static void XawUpdateLayout(Widget w) { XawPrintShellWidget print_shell = (XawPrintShellWidget)w; switch( print_shell->print.layoutmode ) { case XawPrintLAYOUTMODE_NONE: break; case XawPrintLAYOUTMODE_PAGESIZE: XtResizeWidget(w, print_shell->print.page_width, print_shell->print.page_height, w->core.border_width); break; case XawPrintLAYOUTMODE_DRAWABLEAREA: XtConfigureWidget(w, print_shell->print.min_x, print_shell->print.min_y, print_shell->print.max_x - print_shell->print.min_x, print_shell->print.max_y - print_shell->print.min_y, w->core.border_width); break; default: XtAppWarning(XtWidgetToApplicationContext(w), "XawPrintShell: XawUpdateResources: Invalid layout mode\n"); break; } } /* Update widget attributes+properties when the we receive * "Xp attribute change"-events (e.g. paper (size/orientation/etc.) * changed etc.) */ static void XawAttributesNotify(Widget w, XtPointer client, XEvent *evp, Boolean *cont) { XawPrintShellWidget print_shell = (XawPrintShellWidget)w; XPAttributeEvent *xpevp = (XPAttributeEvent *)evp; XawUpdateResources(w, xpevp->context); XawUpdateLayout(w); }