forked from extern/egroupware
* All apps: printing of lists improved a lot, asks now how many lines to print
r51437: Work in progress of printing nextmatches, still needs some prettying up & edge case testing r51453: Bug fixes on nextmatch printing - fix loaded rows check - fix hidden etemplate check r51454: Printing CSS improvements r51588: Attempt to get nextmatch printing always on the page (landscape) r51589: Attempt to get nextmatch printing always on the page (landscape) - put things back if they cancel at nextmatch dialog r51612: disable footer for print
This commit is contained in:
parent
4249dca91d
commit
6625ffdde4
@ -144,3 +144,20 @@ var et2_IDetachedDOM = new Interface({
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface for widgets that need to do something special before printing
|
||||
*/
|
||||
var et2_IPrint = new Interface({
|
||||
/**
|
||||
* Set up for printing
|
||||
*
|
||||
* @return {undefined|Deferred} Return a jQuery Deferred object if not done setting up
|
||||
* (waiting for data)
|
||||
*/
|
||||
beforePrint: function() {},
|
||||
|
||||
/**
|
||||
* Reset after printing
|
||||
*/
|
||||
afterPrint: function() {}
|
||||
});
|
@ -68,7 +68,7 @@ var et2_INextmatchSortable = new Interface({
|
||||
*
|
||||
* @augments et2_DOMWidget
|
||||
*/
|
||||
var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
|
||||
var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput, et2_IPrint],
|
||||
{
|
||||
attributes: {
|
||||
// These normally set in settings, but broken out into attributes to allow run-time changes
|
||||
@ -1836,6 +1836,141 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
|
||||
set_value: function(_value)
|
||||
{
|
||||
this.value = _value;
|
||||
},
|
||||
|
||||
// Printing
|
||||
/**
|
||||
* Prepare for printing
|
||||
*
|
||||
* We check for un-loaded rows, and ask the user what they want to do about them.
|
||||
* If they want to print them all, we ask the server and print when they're loaded.
|
||||
*/
|
||||
beforePrint: function() {
|
||||
// Add the class, if needed
|
||||
this.div.addClass('print');
|
||||
|
||||
// Trigger resize, so we can fit on a page
|
||||
this.dynheight.outerNode.css('max-width',this.div.css('max-width'));
|
||||
this.resize();
|
||||
|
||||
// Check for rows that aren't loaded yet, or lots of rows
|
||||
var range = this.controller._grid.getIndexRange();
|
||||
this.old_height = this.controller._grid._scrollHeight;
|
||||
var loaded_count = range.bottom - range.top +1;
|
||||
var total = this.controller._grid.getTotalCount();
|
||||
if(loaded_count != total ||
|
||||
this.controller._grid.getTotalCount() > 100)
|
||||
{
|
||||
// Defer the printing
|
||||
var defer = jQuery.Deferred();
|
||||
|
||||
// Something not in the grid, lets ask
|
||||
et2_dialog.show_prompt(jQuery.proxy(function(button, value) {
|
||||
if(button == 'dialog[cancel]') {
|
||||
// Give dialog a chance to close, or it will be in the print
|
||||
window.setTimeout(function() {defer.reject();}, 0);
|
||||
return;
|
||||
}
|
||||
value = parseInt(value);
|
||||
if(value > total)
|
||||
{
|
||||
value = total;
|
||||
}
|
||||
|
||||
// If they want the whole thing, treat it as all
|
||||
if(button == 'dialog[ok]' && value == this.controller._grid.getTotalCount())
|
||||
{
|
||||
button = 'dialog[all]';
|
||||
// Add the class, gives more reliable sizing
|
||||
this.div.addClass('print');
|
||||
}
|
||||
// We need more rows
|
||||
if(button == 'dialog[all]' || value > loaded_count)
|
||||
{
|
||||
var count = 0;
|
||||
var fetchedCount = 0;
|
||||
var cancel = false;
|
||||
var nm = this;
|
||||
var dialog = et2_dialog.show_dialog(
|
||||
// Abort the long task if they canceled the data load
|
||||
function() {count = total; cancel=true;window.setTimeout(function() {defer.reject();},0)},
|
||||
egw.lang('Loading'), egw.lang('please wait...'),{},[
|
||||
{"button_id": et2_dialog.CANCEL_BUTTON,"text": 'cancel',id: 'dialog[cancel]',image: 'cancel'}
|
||||
]
|
||||
);
|
||||
|
||||
// dataFetch() is asyncronous, so all these requests just get fired off...
|
||||
// 200 rows chosen arbitrarily to reduce requests.
|
||||
do {
|
||||
var ctx = {
|
||||
"self": this.controller,
|
||||
"start": count,
|
||||
"count": Math.min(value,200),
|
||||
"lastModification": this.controller._lastModification
|
||||
};
|
||||
if(nm.controller.dataStorePrefix)
|
||||
{
|
||||
ctx.prefix = nm.controller.dataStorePrefix;
|
||||
}
|
||||
nm.controller.dataFetch({start:count, num_rows: Math.min(value,200)}, function(data) {
|
||||
// Keep track
|
||||
if(data && data.order)
|
||||
{
|
||||
fetchedCount += data.order.length;
|
||||
}
|
||||
nm.controller._fetchCallback.apply(this, arguments);
|
||||
|
||||
if(fetchedCount >= value)
|
||||
{
|
||||
if(cancel)
|
||||
{
|
||||
dialog.destroy();
|
||||
defer.reject();
|
||||
return;
|
||||
}
|
||||
nm.controller._grid.setScrollHeight(nm.controller._grid.getAverageHeight() * (value+1));
|
||||
// Grid needs to redraw before it can be printed, so wait
|
||||
window.setTimeout(jQuery.proxy(function() {
|
||||
dialog.destroy();
|
||||
// Should be OK to print now
|
||||
defer.resolve();
|
||||
},nm),ET2_GRID_INVALIDATE_TIMEOUT);
|
||||
|
||||
}
|
||||
|
||||
},ctx);
|
||||
count += 200;
|
||||
} while (count < value)
|
||||
nm.controller._grid.setScrollHeight(nm.controller._grid.getAverageHeight() * (value+1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't need more rows, limit to requested and finish
|
||||
this.controller._grid.setScrollHeight(this.controller._grid.getAverageHeight() * (value+1));
|
||||
// Give dialog a chance to close, or it will be in the print
|
||||
window.setTimeout(function() {defer.resolve();}, 0);
|
||||
}
|
||||
//this.controller._gridCallback(0, button == et2_dialog.OK_BUTTON ? value : this.controller._grid.getTotalCount());
|
||||
},this),
|
||||
egw.lang('How many rows to print'), egw.lang('Print'),
|
||||
Math.min(100, total),
|
||||
[
|
||||
{"button_id": 1,"text": egw.lang('Ok'), id: 'dialog[ok]', image: 'check', "default":true},
|
||||
// Nice for small lists, kills server for large lists
|
||||
//{"button_id": 2,"text": egw.lang('All'), id: 'dialog[all]', image: ''},
|
||||
{"button_id": 0,"text": egw.lang('Cancel'), id: 'dialog[cancel]', image: 'cancel'},
|
||||
]
|
||||
);
|
||||
return defer;
|
||||
}
|
||||
// Don't return anything, just work normally
|
||||
},
|
||||
afterPrint: function() {
|
||||
this.div.removeClass('print');
|
||||
this.controller._grid.setScrollHeight(this.old_height);
|
||||
delete this.old_height;
|
||||
this.dynheight.outerNode.css('max-width','inherit');
|
||||
this.resize();
|
||||
}
|
||||
});
|
||||
et2_register_widget(et2_nextmatch, ["nextmatch"]);
|
||||
|
@ -904,6 +904,69 @@ etemplate2.app_refresh = function(_msg, _app, _id, _type)
|
||||
return refresh_done;
|
||||
};
|
||||
|
||||
/**
|
||||
* "Intelligently" print a given app
|
||||
*
|
||||
* Mostly, we let the nextmatch change how many rows it's showing, so you don't
|
||||
* get just one printed page.
|
||||
*/
|
||||
etemplate2.print = function(_app)
|
||||
{
|
||||
// Allow any widget to change for printing
|
||||
var et2 = etemplate2.getByApplication(_app);
|
||||
|
||||
// Sometimes changes take time
|
||||
var deferred = [];
|
||||
for(var i = 0; i < et2.length; i++)
|
||||
{
|
||||
// Skip hidden templates
|
||||
if(!jQuery(et2[i].DOMContainer).filter(':visible').length) continue;
|
||||
|
||||
et2[i].widgetContainer.iterateOver(function(_widget) {
|
||||
// Skip widgets from a different etemplate (home)
|
||||
if(_widget.getInstanceManager() != et2[i]) return;
|
||||
var result = _widget.beforePrint();
|
||||
if (typeof result == "object" && result.done)
|
||||
{
|
||||
deferred.push(result);
|
||||
}
|
||||
},et2,et2_IPrint);
|
||||
}
|
||||
|
||||
// Try to clean up after - not guaranteed
|
||||
var afterPrint = function() {
|
||||
for(var i = 0; i < et2.length; i++)
|
||||
{
|
||||
// Skip hidden templates
|
||||
if(!jQuery(et2[i].DOMContainer).filter(':visible')) continue;
|
||||
et2[i].widgetContainer.iterateOver(function(_widget) {
|
||||
_widget.afterPrint();
|
||||
},et2,et2_IPrint);
|
||||
}
|
||||
var mediaQueryList = window.matchMedia('print');
|
||||
mediaQueryList
|
||||
};
|
||||
if(egw.window.matchMedia) {
|
||||
var mediaQueryList = window.matchMedia('print');
|
||||
var listener = function(mql) {
|
||||
if (!mql.matches) {
|
||||
afterPrint();
|
||||
mediaQueryList.removeListener(listener);
|
||||
}
|
||||
};
|
||||
mediaQueryList.addListener(listener);
|
||||
}
|
||||
|
||||
egw.window.onafterprint = afterPrint;
|
||||
|
||||
// Wait for everything to be loaded, then send it off
|
||||
jQuery.when.apply(jQuery, deferred).done(function() {
|
||||
egw.window.print();
|
||||
}).fail(function() {
|
||||
afterPrint();
|
||||
});
|
||||
}
|
||||
|
||||
// Some static things to make getting into widget context a little easier //
|
||||
|
||||
/**
|
||||
|
@ -462,6 +462,21 @@ which caused click on free space infront of a tag stops nm row selection*/
|
||||
.et2_nextmatch .egwGridView_grid tr td div.et2_vbox a {
|
||||
display: table-row;
|
||||
}
|
||||
/*
|
||||
These are set via javascript before printing to help tame nextmatch's layout
|
||||
for printing
|
||||
*/
|
||||
.et2_nextmatch.print .egwGridView_scrollarea {
|
||||
height: auto !important;
|
||||
width: auto !important;
|
||||
}
|
||||
.et2_nextmatch.print > div {
|
||||
height: auto !important;
|
||||
}
|
||||
.et2_nextmatch.print {
|
||||
/* This is fairly arbitrary, but makes it fit in Chrome and Firefox*/
|
||||
max-width: 700pt !important;
|
||||
}
|
||||
/**
|
||||
* Diff widget
|
||||
*/
|
||||
@ -1115,13 +1130,19 @@ div.message.floating {
|
||||
.et2_nextmatch .egwGridView_grid > tbody > tr {
|
||||
display: block;
|
||||
}
|
||||
.et2_nextmatch .egwGridView_spacer {
|
||||
display:none;
|
||||
}
|
||||
.egwGridView_grid > tbody > tr {
|
||||
page-break-inside: avoid;
|
||||
-webkit-region-break-inside: avoid;
|
||||
}
|
||||
.et2_nextmatch > div {
|
||||
width: 100% !important;
|
||||
height: auto;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
.et2_nextmatch table.egwGridView_outer thead tr th div.innerContainer {
|
||||
max-height: inherit;
|
||||
}
|
||||
#cke_1_top.cke_top {
|
||||
display: none;
|
||||
|
@ -18,21 +18,35 @@
|
||||
@media print {
|
||||
|
||||
html, body {
|
||||
width: 100% !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
#egw_fw_sidebar, #egw_fw_topmenu, .egw_fw_ui_tabs_header, #egw_fw_topmenu_slide,#egw_fw_print,#egw_fw_logout {
|
||||
display: none;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
#egw_fw_main, #egw_fw_tabs {
|
||||
margin: 0px !important;
|
||||
float: none !important;
|
||||
width: auto !important;
|
||||
}
|
||||
#egw_fw_basecontainer {
|
||||
position: inherit;
|
||||
width: auto;
|
||||
}
|
||||
.egw_fw_ui_tab_content, .egw_fw_ui_tab_content > div {
|
||||
height: auto;
|
||||
.egw_fw_ui_tab_content {
|
||||
position: relative;
|
||||
float: none;
|
||||
width: auto !important;
|
||||
}
|
||||
#egw_fw_main .egw_fw_ui_tab_content > div {
|
||||
width: auto !important;
|
||||
}
|
||||
.egw_fw_ui_tab_content, .egw_fw_ui_tab_content div.egw_fw_content_browser_div {
|
||||
height: auto !important;
|
||||
overflow-y: hidden;
|
||||
position: relative;
|
||||
}
|
||||
body {
|
||||
background: white;
|
||||
|
@ -958,8 +958,18 @@ var fw_base = Class.extend({
|
||||
if (appWindow)
|
||||
{
|
||||
appWindow.focus();
|
||||
|
||||
// et2 available, let its widgets prepare
|
||||
if(typeof etemplate2 == "function" && etemplate2.print)
|
||||
{
|
||||
etemplate2.print(this.activeApp.appName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Print
|
||||
appWindow.print();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
@media print {
|
||||
#egw_fw_topmenu_addons, #egw_fw_header {
|
||||
#egw_fw_topmenu_addons, #egw_fw_header, #egw_fw_footer {
|
||||
display: none;
|
||||
}
|
||||
} /* @media print */
|
Loading…
Reference in New Issue
Block a user