Browse Source

Resolve merge conflict

Neal Wilson 6 years ago
parent
commit
5baf5443b0

+ 3
- 0
config/ImportFilters/Example.js View File

1
+print(pluginContext.text);
2
+print("A plugin has run\n");
3
+pluginContext.cq.trigger();

+ 21
- 0
config/ImportFilters/Ikawa.js View File

1
+pluginContext.table.setHeaderData(0, "Time");
2
+pluginContext.table.setHeaderData(1, "Temperature");
3
+pluginContext.table.setHeaderData(2, "Set");
4
+pluginContext.table.setHeaderData(3, "Fan");
5
+pluginContext.table.setHeaderData(4, "Heater");
6
+pluginContext.table.setHeaderData(5, "Note");
7
+var lines = pluginContext.data.split('\n');
8
+for(var i = 0; i < lines.length; i++) {
9
+	var fields = lines[i].split(',');
10
+	if(fields[5] == "roasting") {
11
+		var time = new QTime;
12
+		time = time.addSecs(Number(fields[0]));
13
+		pluginContext.newMeasurement(new Measurement(Units.convertTemperature(fields[4], Units.Celsius, Units.Fahrenheit), time), 1);
14
+		pluginContext.newMeasurement(new Measurement(Units.convertTemperature(fields[2], Units.Celsius, Units.Fahrenheit), time), 2);
15
+		pluginContext.newMeasurement(new Measurement(fields[1], time, Units.Unitless), 3);
16
+		pluginContext.newMeasurement(new Measurement(fields[6], time, Units.Unitless), 4);
17
+	}
18
+}
19
+for(var i = 1; i < 5; i++) {
20
+	pluginContext.table.newAnnotation("End", i, 5);
21
+}

+ 3
- 1
config/Reports/itemtransactions.xml View File

222
                     }, 0));
222
                     }, 0));
223
                     output.writeEndElement();
223
                     output.writeEndElement();
224
                     output.writeEndElement();
224
                     output.writeEndElement();
225
-                    query.prepare("SELECT time::date, type, quantity / :c1, balance / :c2, (SELECT files FROM roasting_log WHERE roasting_log.time = item_history.time AND item = ANY(unroasted_id)), (SELECT invoice_id FROM invoice_items WHERE item = item_id AND item_history.type = 'PURCHASE'), (SELECT vendor || ' ' || invoice FROM invoices WHERE id = (SELECT invoice_id FROM invoice_items WHERE item = item_id AND item_history.type = 'PURCHASE')), (SELECT name FROM items WHERE id = (SELECT roasted_id FROM roasting_log WHERE roasting_log.time = item_history.time AND item = ANY(unroasted_id))), customer, reason FROM item_history(:item) WHERE time >= :sd AND time < :ed ::date + interval '1 day'");
225
+                    query.prepare("SELECT time::date, type, quantity / :c1, balance / :c2, (SELECT files FROM roasting_log WHERE roasting_log.time = item_history.time AND item = ANY(unroasted_id)), (SELECT invoice_id FROM invoice_items WHERE item = item_id AND item_history.type = 'PURCHASE'), (SELECT vendor || ' ' || invoice FROM invoices WHERE id = (SELECT invoice_id FROM invoice_items WHERE item = item_id AND item_history.type = 'PURCHASE')), (SELECT name FROM items WHERE id = (SELECT roasted_id FROM roasting_log WHERE roasting_log.time = item_history.time AND item = ANY(unroasted_id))), customer, reason, (SELECT person FROM transactions WHERE time = item_history.time AND item = item_history.item) FROM item_history(:item) WHERE time >= :sd AND time < :ed ::date + interval '1 day'");
226
                     query.bind(":sd", startDate);
226
                     query.bind(":sd", startDate);
227
                     query.bind(":ed", endDate);
227
                     query.bind(":ed", endDate);
228
                     switch(unitBox.currentIndex)
228
                     switch(unitBox.currentIndex)
245
                     output.writeTextElement("th", TTR("item_transactions", "Quantity"));
245
                     output.writeTextElement("th", TTR("item_transactions", "Quantity"));
246
                     output.writeTextElement("th", TTR("item_transactions", "Balance"));
246
                     output.writeTextElement("th", TTR("item_transactions", "Balance"));
247
                     output.writeTextElement("th", TTR("item_transactions", "Record"));
247
                     output.writeTextElement("th", TTR("item_transactions", "Record"));
248
+					output.writeTextElement("th", TTR("item_transactions", "Person"));
248
                     output.writeEndElement(); // tr
249
                     output.writeEndElement(); // tr
249
                     var prev_balance = "0";
250
                     var prev_balance = "0";
250
                     var prev_prec = 0;
251
                     var prev_prec = 0;
297
                         } else {
298
                         } else {
298
                             output.writeTextElement("td", "");
299
                             output.writeTextElement("td", "");
299
                         }
300
                         }
301
+						output.writeTextElement("td", query.value(10));
300
                         output.writeEndElement(); // tr
302
                         output.writeEndElement(); // tr
301
                     }
303
                     }
302
                     output.writeEndElement(); // table
304
                     output.writeEndElement(); // table

+ 38
- 0
config/Scripts/batchtag.css View File

1
+body, h1, h2, h3, h4, h5, h6,
2
+p, blockquote, pre, hr,
3
+dl, dd, ol, ul, figure {
4
+  margin: 0;
5
+  padding: 0;
6
+}
7
+*,
8
+*::before,
9
+*::after {
10
+    -webkit-box-sizing: border-box;
11
+    -moz-box-sizing: border-box;
12
+    box-sizing: border-box;
13
+}
14
+
15
+body {
16
+  width: 58mm;
17
+}
18
+
19
+h1 {
20
+  padding-top: 10mm;
21
+  font-weight: 400;
22
+  font-size: 5mm;
23
+  text-align: center;
24
+}
25
+
26
+span {
27
+  display: block;
28
+  font-size: 4mm;
29
+  margin-left: 1mm;
30
+  margin-right: 1mm;
31
+}
32
+
33
+#container {
34
+	width: 190px;
35
+	margin-left: auto;
36
+	margin-right: auto;
37
+}
38
+

+ 346
- 0
config/Scripts/qrcode.js View File

1
+/**
2
+ * @fileoverview
3
+ * - modified davidshimjs/qrcodejs library for use in node.js
4
+ * - Using the 'QRCode for Javascript library'
5
+ * - Fixed dataset of 'QRCode for Javascript library' for support full-spec.
6
+ * - this library has no dependencies.
7
+ *
8
+ * @version 0.9.1 (2016-02-12)
9
+ * @author davidshimjs, papnkukn
10
+ * @see <a href="http://www.d-project.com/" target="_blank">http://www.d-project.com/</a>
11
+ * @see <a href="http://jeromeetienne.github.com/jquery-qrcode/" target="_blank">http://jeromeetienne.github.com/jquery-qrcode/</a>
12
+ * @see <a href="https://github.com/davidshimjs/qrcodejs" target="_blank">https://github.com/davidshimjs/qrcodejs</a>
13
+ */
14
+
15
+//---------------------------------------------------------------------
16
+// QRCode for JavaScript
17
+//
18
+// Copyright (c) 2009 Kazuhiko Arase
19
+//
20
+// URL: http://www.d-project.com/
21
+//
22
+// Licensed under the MIT license:
23
+//   http://www.opensource.org/licenses/mit-license.php
24
+//
25
+// The word "QR Code" is registered trademark of 
26
+// DENSO WAVE INCORPORATED
27
+//   http://www.denso-wave.com/qrcode/faqpatent-e.html
28
+//
29
+// Modified by Neal Wilson to replace the width and height attributes
30
+// of the generated svg with a viewbox for easier scaling.
31
+//
32
+//---------------------------------------------------------------------
33
+function QR8bitByte(data) {
34
+  this.mode = QRMode.MODE_8BIT_BYTE;
35
+  this.data = data;
36
+  this.parsedData = [];
37
+
38
+  // Added to support UTF-8 Characters
39
+  for (var i = 0, l = this.data.length; i < l; i++) {
40
+    var byteArray = [];
41
+    var code = this.data.charCodeAt(i);
42
+
43
+    if (code > 0x10000) {
44
+      byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18);
45
+      byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12);
46
+      byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6);
47
+      byteArray[3] = 0x80 | (code & 0x3F);
48
+    } else if (code > 0x800) {
49
+      byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12);
50
+      byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6);
51
+      byteArray[2] = 0x80 | (code & 0x3F);
52
+    } else if (code > 0x80) {
53
+      byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6);
54
+      byteArray[1] = 0x80 | (code & 0x3F);
55
+    } else {
56
+      byteArray[0] = code;
57
+    }
58
+
59
+    this.parsedData.push(byteArray);
60
+  }
61
+
62
+  this.parsedData = Array.prototype.concat.apply([], this.parsedData);
63
+
64
+  if (this.parsedData.length != this.data.length) {
65
+    this.parsedData.unshift(191);
66
+    this.parsedData.unshift(187);
67
+    this.parsedData.unshift(239);
68
+  }
69
+}
70
+
71
+QR8bitByte.prototype = {
72
+  getLength: function (buffer) {
73
+    return this.parsedData.length;
74
+  },
75
+  write: function (buffer) {
76
+    for (var i = 0, l = this.parsedData.length; i < l; i++) {
77
+      buffer.put(this.parsedData[i], 8);
78
+    }
79
+  }
80
+};
81
+
82
+function QRCodeModel(typeNumber, errorCorrectLevel) {
83
+  this.typeNumber = typeNumber;
84
+  this.errorCorrectLevel = errorCorrectLevel;
85
+  this.modules = null;
86
+  this.moduleCount = 0;
87
+  this.dataCache = null;
88
+  this.dataList = [];
89
+}
90
+
91
+QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);}
92
+return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row<this.moduleCount;row++){this.modules[row]=new Array(this.moduleCount);for(var col=0;col<this.moduleCount;col++){this.modules[row][col]=null;}}
93
+this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(test,maskPattern);if(this.typeNumber>=7){this.setupTypeNumber(test);}
94
+if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);}
95
+this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}}
96
+return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row<this.modules.length;row++){var y=row*cs;for(var col=0;col<this.modules[row].length;col++){var x=col*cs;var dark=this.modules[row][col];if(dark){qr_mc.beginFill(0,100);qr_mc.moveTo(x,y);qr_mc.lineTo(x+cs,y);qr_mc.lineTo(x+cs,y+cs);qr_mc.lineTo(x,y+cs);qr_mc.endFill();}}}
97
+return qr_mc;},setupTimingPattern:function(){for(var r=8;r<this.moduleCount-8;r++){if(this.modules[r][6]!=null){continue;}
98
+this.modules[r][6]=(r%2==0);}
99
+for(var c=8;c<this.moduleCount-8;c++){if(this.modules[6][c]!=null){continue;}
100
+this.modules[6][c]=(c%2==0);}},setupPositionAdjustPattern:function(){var pos=QRUtil.getPatternPosition(this.typeNumber);for(var i=0;i<pos.length;i++){for(var j=0;j<pos.length;j++){var row=pos[i];var col=pos[j];if(this.modules[row][col]!=null){continue;}
101
+for(var r=-2;r<=2;r++){for(var c=-2;c<=2;c++){if(r==-2||r==2||c==-2||c==2||(r==0&&c==0)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}}}},setupTypeNumber:function(test){var bits=QRUtil.getBCHTypeNumber(this.typeNumber);for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;}
102
+for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}}
103
+for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}}
104
+this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex<data.length){dark=(((data[byteIndex]>>>bitIndex)&1)==1);}
105
+var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;}
106
+this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}}
107
+row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;i<dataList.length;i++){var data=dataList[i];buffer.put(data.mode,4);buffer.put(data.getLength(),QRUtil.getLengthInBits(data.mode,typeNumber));data.write(buffer);}
108
+var totalDataCount=0;for(var i=0;i<rsBlocks.length;i++){totalDataCount+=rsBlocks[i].dataCount;}
109
+if(buffer.getLengthInBits()>totalDataCount*8){throw new Error("code length overflow. ("
110
++buffer.getLengthInBits()
111
++">"
112
++totalDataCount*8
113
++")");}
114
+if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);}
115
+while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);}
116
+while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;}
117
+buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;}
118
+buffer.put(QRCodeModel.PAD1,8);}
119
+return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r<rsBlocks.length;r++){var dcCount=rsBlocks[r].dataCount;var ecCount=rsBlocks[r].totalCount-dcCount;maxDcCount=Math.max(maxDcCount,dcCount);maxEcCount=Math.max(maxEcCount,ecCount);dcdata[r]=new Array(dcCount);for(var i=0;i<dcdata[r].length;i++){dcdata[r][i]=0xff&buffer.buffer[i+offset];}
120
+offset+=dcCount;var rsPoly=QRUtil.getErrorCorrectPolynomial(ecCount);var rawPoly=new QRPolynomial(dcdata[r],rsPoly.getLength()-1);var modPoly=rawPoly.mod(rsPoly);ecdata[r]=new Array(rsPoly.getLength()-1);for(var i=0;i<ecdata[r].length;i++){var modIndex=i+modPoly.getLength()-ecdata[r].length;ecdata[r][i]=(modIndex>=0)?modPoly.get(modIndex):0;}}
121
+var totalCodeCount=0;for(var i=0;i<rsBlocks.length;i++){totalCodeCount+=rsBlocks[i].totalCount;}
122
+var data=new Array(totalCodeCount);var index=0;for(var i=0;i<maxDcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<dcdata[r].length){data[index++]=dcdata[r][i];}}}
123
+for(var i=0;i<maxEcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<ecdata[r].length){data[index++]=ecdata[r][i];}}}
124
+return data;};var QRMode={MODE_NUMBER:1<<0,MODE_ALPHA_NUM:1<<1,MODE_8BIT_BYTE:1<<2,MODE_KANJI:1<<3};var QRErrorCorrectLevel={L:1,M:0,Q:3,H:2};var QRMaskPattern={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7};var QRUtil={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:(1<<10)|(1<<8)|(1<<5)|(1<<4)|(1<<2)|(1<<1)|(1<<0),G18:(1<<12)|(1<<11)|(1<<10)|(1<<9)|(1<<8)|(1<<5)|(1<<2)|(1<<0),G15_MASK:(1<<14)|(1<<12)|(1<<10)|(1<<4)|(1<<1),getBCHTypeInfo:function(data){var d=data<<10;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)>=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));}
125
+return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));}
126
+return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;}
127
+return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i<errorCorrectLength;i++){a=a.multiply(new QRPolynomial([1,QRMath.gexp(i)],0));}
128
+return a;},getLengthInBits:function(mode,type){if(1<=type&&type<10){switch(mode){case QRMode.MODE_NUMBER:return 10;case QRMode.MODE_ALPHA_NUM:return 9;case QRMode.MODE_8BIT_BYTE:return 8;case QRMode.MODE_KANJI:return 8;default:throw new Error("mode:"+mode);}}else if(type<27){switch(mode){case QRMode.MODE_NUMBER:return 12;case QRMode.MODE_ALPHA_NUM:return 11;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 10;default:throw new Error("mode:"+mode);}}else if(type<41){switch(mode){case QRMode.MODE_NUMBER:return 14;case QRMode.MODE_ALPHA_NUM:return 13;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 12;default:throw new Error("mode:"+mode);}}else{throw new Error("type:"+type);}},getLostPoint:function(qrCode){var moduleCount=qrCode.getModuleCount();var lostPoint=0;for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount;col++){var sameCount=0;var dark=qrCode.isDark(row,col);for(var r=-1;r<=1;r++){if(row+r<0||moduleCount<=row+r){continue;}
129
+for(var c=-1;c<=1;c++){if(col+c<0||moduleCount<=col+c){continue;}
130
+if(r==0&&c==0){continue;}
131
+if(dark==qrCode.isDark(row+r,col+c)){sameCount++;}}}
132
+if(sameCount>5){lostPoint+=(3+sameCount-5);}}}
133
+for(var row=0;row<moduleCount-1;row++){for(var col=0;col<moduleCount-1;col++){var count=0;if(qrCode.isDark(row,col))count++;if(qrCode.isDark(row+1,col))count++;if(qrCode.isDark(row,col+1))count++;if(qrCode.isDark(row+1,col+1))count++;if(count==0||count==4){lostPoint+=3;}}}
134
+for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount-6;col++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row,col+1)&&qrCode.isDark(row,col+2)&&qrCode.isDark(row,col+3)&&qrCode.isDark(row,col+4)&&!qrCode.isDark(row,col+5)&&qrCode.isDark(row,col+6)){lostPoint+=40;}}}
135
+for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount-6;row++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row+1,col)&&qrCode.isDark(row+2,col)&&qrCode.isDark(row+3,col)&&qrCode.isDark(row+4,col)&&!qrCode.isDark(row+5,col)&&qrCode.isDark(row+6,col)){lostPoint+=40;}}}
136
+var darkCount=0;for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount;row++){if(qrCode.isDark(row,col)){darkCount++;}}}
137
+var ratio=Math.abs(100*darkCount/moduleCount/moduleCount-50)/5;lostPoint+=ratio*10;return lostPoint;}};var QRMath={glog:function(n){if(n<1){throw new Error("glog("+n+")");}
138
+return QRMath.LOG_TABLE[n];},gexp:function(n){while(n<0){n+=255;}
139
+while(n>=256){n-=255;}
140
+return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<<i;}
141
+for(var i=8;i<256;i++){QRMath.EXP_TABLE[i]=QRMath.EXP_TABLE[i-4]^QRMath.EXP_TABLE[i-5]^QRMath.EXP_TABLE[i-6]^QRMath.EXP_TABLE[i-8];}
142
+for(var i=0;i<255;i++){QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]]=i;}
143
+function QRPolynomial(num,shift){if(num.length==undefined){throw new Error(num.length+"/"+shift);}
144
+var offset=0;while(offset<num.length&&num[offset]==0){offset++;}
145
+this.num=new Array(num.length-offset+shift);for(var i=0;i<num.length-offset;i++){this.num[i]=num[i+offset];}}
146
+QRPolynomial.prototype={get:function(index){return this.num[index];},getLength:function(){return this.num.length;},multiply:function(e){var num=new Array(this.getLength()+e.getLength()-1);for(var i=0;i<this.getLength();i++){for(var j=0;j<e.getLength();j++){num[i+j]^=QRMath.gexp(QRMath.glog(this.get(i))+QRMath.glog(e.get(j)));}}
147
+return new QRPolynomial(num,0);},mod:function(e){if(this.getLength()-e.getLength()<0){return this;}
148
+var ratio=QRMath.glog(this.get(0))-QRMath.glog(e.get(0));var num=new Array(this.getLength());for(var i=0;i<this.getLength();i++){num[i]=this.get(i);}
149
+for(var i=0;i<e.getLength();i++){num[i]^=QRMath.gexp(QRMath.glog(e.get(i))+ratio);}
150
+return new QRPolynomial(num,0).mod(e);}};function QRRSBlock(totalCount,dataCount){this.totalCount=totalCount;this.dataCount=dataCount;}
151
+QRRSBlock.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];QRRSBlock.getRSBlocks=function(typeNumber,errorCorrectLevel){var rsBlock=QRRSBlock.getRsBlockTable(typeNumber,errorCorrectLevel);if(rsBlock==undefined){throw new Error("bad rs block @ typeNumber:"+typeNumber+"/errorCorrectLevel:"+errorCorrectLevel);}
152
+var length=rsBlock.length/3;var list=[];for(var i=0;i<length;i++){var count=rsBlock[i*3+0];var totalCount=rsBlock[i*3+1];var dataCount=rsBlock[i*3+2];for(var j=0;j<count;j++){list.push(new QRRSBlock(totalCount,dataCount));}}
153
+return list;};QRRSBlock.getRsBlockTable=function(typeNumber,errorCorrectLevel){switch(errorCorrectLevel){case QRErrorCorrectLevel.L:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+0];case QRErrorCorrectLevel.M:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+1];case QRErrorCorrectLevel.Q:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+2];case QRErrorCorrectLevel.H:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+3];default:return undefined;}};function QRBitBuffer(){this.buffer=[];this.length=0;}
154
+QRBitBuffer.prototype={get:function(index){var bufIndex=Math.floor(index/8);return((this.buffer[bufIndex]>>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i<length;i++){this.putBit(((num>>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);}
155
+if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));}
156
+this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]];
157
+
158
+
159
+/** Constructor */
160
+function QRCode(options) {
161
+  var instance = this;
162
+  
163
+  //Default options
164
+  this.options = {
165
+    padding: 4,
166
+    width: 256, 
167
+    height: 256,
168
+    typeNumber: 4,
169
+    color: "#000000",
170
+    background: "#ffffff",
171
+    ecl: "M"
172
+  };
173
+  
174
+  //In case the options is string
175
+  if (typeof options === 'string') {
176
+    options = {
177
+      content: options
178
+    };
179
+  }
180
+  
181
+  //Merge options
182
+  if (options) {
183
+    for (var i in options) {
184
+      this.options[i] = options[i];
185
+    }
186
+  }
187
+  
188
+  if (typeof this.options.content !== 'string') {
189
+    throw new Error("Expected 'content' as string!");
190
+  }
191
+  
192
+  if (this.options.content.length === 0 /* || this.options.content.length > 7089 */) {
193
+    throw new Error("Expected 'content' to be non-empty!");
194
+  }
195
+  
196
+  if (!(this.options.padding >= 0)) {
197
+    throw new Error("Expected 'padding' value to be non-negative!");
198
+  }
199
+  
200
+  if (!(this.options.width > 0) || !(this.options.height > 0)) {
201
+    throw new Error("Expected 'width' or 'height' value to be higher than zero!");
202
+  }
203
+  
204
+  //Gets the error correction level
205
+  function _getErrorCorrectLevel(ecl) {
206
+    switch (ecl) {
207
+        case "L":
208
+          return QRErrorCorrectLevel.L;
209
+          
210
+        case "M":
211
+          return QRErrorCorrectLevel.M;
212
+          
213
+        case "Q":
214
+          return QRErrorCorrectLevel.Q;
215
+          
216
+        case "H":
217
+          return QRErrorCorrectLevel.H;
218
+          
219
+        default:
220
+          throw new Error("Unknwon error correction level: " + ecl);
221
+      }
222
+  }
223
+  
224
+  //Get type number
225
+  function _getTypeNumber(content, ecl) {      
226
+    var length = _getUTF8Length(content);
227
+    
228
+    var type = 1;
229
+    var limit = 0;
230
+    for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) {
231
+      var table = QRCodeLimitLength[i];
232
+      if (!table) {
233
+        throw new Error("Content too long: expected " + limit + " but got " + length);
234
+      }
235
+      
236
+      switch (ecl) {
237
+        case "L":
238
+          limit = table[0];
239
+          break;
240
+          
241
+        case "M":
242
+          limit = table[1];
243
+          break;
244
+          
245
+        case "Q":
246
+          limit = table[2];
247
+          break;
248
+          
249
+        case "H":
250
+          limit = table[3];
251
+          break;
252
+          
253
+        default:
254
+          throw new Error("Unknwon error correction level: " + ecl);
255
+      }
256
+      
257
+      if (length <= limit) {
258
+        break;
259
+      }
260
+      
261
+      type++;
262
+    }
263
+    
264
+    if (type > QRCodeLimitLength.length) {
265
+      throw new Error("Content too long");
266
+    }
267
+    
268
+    return type;
269
+  }
270
+
271
+  //Gets text length
272
+  function _getUTF8Length(content) {
273
+    var result = encodeURI(content).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a');
274
+    return result.length + (result.length != content ? 3 : 0);
275
+  }
276
+  
277
+  //Generate QR Code matrix
278
+  var content = this.options.content;
279
+  var type = _getTypeNumber(content, this.options.ecl);
280
+  var ecl = _getErrorCorrectLevel(this.options.ecl);
281
+  this.qrcode = new QRCodeModel(type, ecl);
282
+  this.qrcode.addData(content);
283
+  this.qrcode.make();
284
+}
285
+
286
+/** Generates QR Code as SVG image */
287
+QRCode.prototype.svg = function(opt) {
288
+  if (typeof opt == "undefined") {
289
+    opt = { container: "svg" };
290
+  }
291
+  
292
+  var options = this.options;
293
+  var modules = this.qrcode.modules;
294
+  
295
+  var EOL = '\r\n';
296
+  var width = options.width;
297
+  var height = options.height;
298
+  var length = modules.length;
299
+  var xsize = width / (length + 2 * options.padding);
300
+  var ysize = height / (length + 2 * options.padding);
301
+  
302
+  var rect = '<rect x="0" y="0" width="' + width + '" height="' + height + '" style="fill:' + options.background + ';shape-rendering:crispEdges;"/>' + EOL;
303
+
304
+  for (var y = 0; y < length; y++) {
305
+    for (var x = 0; x < length; x++) {
306
+      var module = modules[x][y];
307
+      if (module) {
308
+        var px = (x * xsize + options.padding * xsize).toString();
309
+        var py = (y * ysize + options.padding * ysize).toString();
310
+        rect += '<rect x="' + px + '" y="' + py + '" width="' + xsize + '" height="' + ysize + '" style="fill:' + options.color + ';shape-rendering:crispEdges;"/>' + EOL;
311
+      }
312
+    }
313
+  }
314
+
315
+  var svg = "";
316
+  switch (opt.container) {
317
+    case "svg":
318
+      svg += '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 ' + width + ' ' + height + '">' + EOL;
319
+      svg += rect;
320
+      svg += '</svg>';
321
+      break;
322
+      
323
+    case "g":
324
+      svg += '<g width="' + width + '" height="' + height + '">' + EOL;
325
+      svg += rect;
326
+      svg += '</g>';
327
+      break;
328
+      
329
+    default:
330
+      svg += rect;
331
+      break;
332
+  }
333
+  
334
+  return svg;
335
+};
336
+
337
+/** Writes QR Code image to a file */
338
+QRCode.prototype.save = function(file, callback) {
339
+  var data = this.svg();
340
+  var fs = require('fs');
341
+  fs.writeFile(file, data, callback);
342
+};
343
+
344
+if (typeof module != "undefined") {
345
+  module.exports = QRCode;
346
+}

+ 434
- 0
config/Windows/manuallogentry.xml View File

1
+<window id="manualLogEntry">
2
+	<menu name="File">
3
+		<plugins id="pluginMenu" title="Import" src="ImportFilters" preRun="pluginContext.preRun();" postRun="pluginContext.postRun();"/>
4
+		<separator />
5
+		<item id="quitItem" shortcut="ctrl+Q">Quit</item>
6
+	</menu>
7
+	<menu name="Log">
8
+        <item id="clear" shortcut="Ctrl+L">Clear Log</item>
9
+        <separator />
10
+        <item id="ms">Millisecond View</item>
11
+        <item id="1s">1 Second View</item>
12
+        <item id="5s">5 Second View</item>
13
+        <item id="10s">10 Second View</item>
14
+        <item id="15s">15 Second View</item>
15
+        <item id="30s">30 Second View</item>
16
+        <item id="1m">1 Minute View</item>
17
+    </menu>
18
+	<layout type="vertical">
19
+		<tabbar id="tabs"/>
20
+		<layout type="stack" id="pages">
21
+			<page>
22
+				<layout type="vertical">
23
+					<layout type="horizontal">
24
+						<label>Batch Type:</label>
25
+						<sqldrop id="batchType" />
26
+						<label>Machine:</label>
27
+						<sqldrop id="machineSelector" />
28
+						<stretch />
29
+					</layout>
30
+					<label>Green Coffee:</label>
31
+					<layout type="stack" id="greenInfoLayout">
32
+						<page id="sampleGreen">
33
+							<layout type="vertical">
34
+								<layout type="grid">
35
+									<row>
36
+										<column><label>Name:</label></column>
37
+										<column><line id="sampleGreenName" /></column>
38
+									</row>
39
+									<row>
40
+										<column><label>Weight:</label></column>
41
+										<column><line id="sampleGreenWeight" validator="numeric">0.0</line></column>
42
+										<column><sqldrop id="sampleGreenUnit" /></column>
43
+									</row>
44
+									<row>
45
+										<column><label>Vendor:</label></column>
46
+										<column><line id="sampleGreenVendor" /></column>
47
+									</row>
48
+									<row>
49
+										<column><label>Arrival Date:</label></column>
50
+										<column><calendar id="sampleGreenArrivalDate" /></column>
51
+									</row>
52
+								</layout>
53
+								<label>Additional Details:</label>
54
+								<sqltablearray columns="2" id="attributes">
55
+									<column name="Attribute" />
56
+									<column name="Value" />
57
+								</sqltablearray>
58
+							</layout>
59
+						</page>
60
+						<page id="productionGreen">
61
+							<layout type="vertical">
62
+								<layout type="horizontal">
63
+									<label>Unit:</label>
64
+									<sqldrop id="productionGreenUnit" />
65
+									<stretch />
66
+								</layout>
67
+								<sqltablearray columns="2" id="productionGreenTable">
68
+									<column name="Coffee" delegate="sql" showdata="true" null="true" nulltext="Delete" nulldata="delete" data="0" display="1">
69
+										<![CDATA[SELECT id, name FROM coffees WHERE quantity <> 0 ORDER BY name]]>
70
+									</column>
71
+									<column name="Weight" delegate="numeric" />
72
+								</sqltablearray>
73
+							</layout>
74
+						</page>
75
+					</layout>
76
+					<label>Roasting Details:</label>
77
+					<layout type="grid">
78
+						<row>
79
+							<column><label>Item:</label></column>
80
+							<column>
81
+								<sqldrop data="0" display="1" showdata="true" id="roastedItem">
82
+									<null />
83
+									<query>SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id IN (SELECT item FROM current_items) ORDER BY name</query>
84
+								</sqldrop>
85
+							</column>
86
+						</row>
87
+						<row>
88
+							<column><label>Weight:</label></column>
89
+							<column><line id="roastedWeight" validator="numeric">0.0</line></column>
90
+						</row>
91
+						<row>
92
+							<column><label>Time:</label></column>
93
+							<column><calendar id="roastTime" time="true"/></column>
94
+						</row>
95
+						<row>
96
+							<column><label>Duration:</label></column>
97
+							<column><timeedit id="roastDuration" /></column>
98
+						</row>
99
+						<row>
100
+							<column><label>Notes:</label></column>
101
+							<column><textarea id="notes" /></column>
102
+						</row>
103
+					</layout>
104
+				</layout>
105
+			</page>
106
+			<page>
107
+				<layout type="vertical">
108
+					<layout type="horizontal">
109
+						<label>Time Increment (s):</label>
110
+						<line id="timeincrement" validator="numeric">30</line>
111
+						<stretch />
112
+						<label>Time:</label>
113
+						<timeedit id="currenttime" />
114
+						<stretch />
115
+						<label>Temperature:</label>
116
+						<line id="currenttemperature" validator="numeric" />
117
+						<label>Note:</label>
118
+						<line id="currentnote" />
119
+						<button name="Add Measurement" id="addmeasurement" type="push" />
120
+					</layout>
121
+					<splitter type="horizontal" id="roastdatasplit">
122
+						<measurementtable id="log" />
123
+						<graph id="graph" />
124
+					</splitter>
125
+				</layout>
126
+			</page>
127
+		</layout>
128
+		<layout type="horizontal">
129
+			<stretch />
130
+			<button name="Submit" id="submit" type="push" />
131
+		</layout>
132
+	</layout>
133
+	<program>
134
+	<![CDATA[
135
+		var window = this;
136
+		this.windowTitle = "Typica - Manual Log Entry";
137
+		window.windowReady.connect(function() {
138
+			if(machineModel.rowCount() == 0) {
139
+				displayError(TTR("manualLogEntry", "Configuration Required"),
140
+				TTR("manualLogEntry", "Please configure a roaster."));
141
+				window.close();
142
+			}
143
+		});
144
+		quitItem = findChildObject(this, 'quitItem');
145
+		quitItem.triggered.connect(function() {
146
+			Application.quit();
147
+		});
148
+		pluginContext = {};
149
+		pluginContext.table = findChildObject(this, 'log');
150
+		pluginContext.table.setHeaderData(1, "Temp");
151
+		pluginContext.table.setHeaderData(2, "Note");
152
+		pluginContext.graph = findChildObject(this, 'graph');
153
+		pluginContext.preRun = function() {
154
+			var filename = QFileDialog.getOpenFileName(window, TTR("manualLogEntry", "Import"), QSettings.value('script/lastDir', '') + '/');
155
+			var file = new QFile(filename);
156
+			if(file.open(1)) {
157
+				pluginContext.data = file.readToString();
158
+				file.close();
159
+				pluginContext.table.clear();
160
+				pluginContext.graph.clear();
161
+				QSettings.setValue("script/lastDir", dir(filename));
162
+			} else {
163
+				throw new Error("Failed to open file, aborting import.");
164
+			}
165
+		};
166
+		pluginContext.postRun = function() {
167
+			
168
+		};
169
+		pluginContext.newMeasurement = function(m, c) {
170
+			pluginContext.table.newMeasurement(m, c);
171
+			pluginContext.graph.newMeasurement(m, c);
172
+		}
173
+		pluginMenu = findChildObject(this, 'pluginMenu');
174
+		pluginMenu.setProperty("activationObject", pluginContext);
175
+		tabs = findChildObject(this, 'tabs');
176
+		tabs.addTab("Batch Data");
177
+		tabs.addTab("Roast Data");
178
+		pages = findChildObject(this, 'pages');
179
+		tabs.currentChanged.connect(function(index) {
180
+			pages.setCurrentIndex(index);
181
+		});
182
+		greenInfoLayout = findChildObject(this, 'greenInfoLayout');
183
+		roastedItem = findChildObject(this, 'roastedItem');
184
+		batchType = findChildObject(this, 'batchType');
185
+		batchType.addItem("Sample");
186
+		batchType.addItem("Production");
187
+		batchType['currentIndexChanged(int)'].connect(function(batchTypeIndex) {
188
+			QSettings.setValue("script/manual_batchType", batchTypeIndex);
189
+			greenInfoLayout.setCurrentIndex(batchTypeIndex);
190
+			roastedItem.enabled = (batchTypeIndex == 1);
191
+		});
192
+		batchType.setCurrentIndex(QSettings.value("script/manual_batchType", 1));
193
+		var machineSelector = findChildObject(this, 'machineSelector');
194
+        var machineModel = new DeviceTreeModel;
195
+        machineSelector.setModel(machineModel);
196
+        machineSelector.currentIndex = QSettings.value("script/manualMachineSelection", 0);
197
+        machineSelector['currentIndexChanged(int)'].connect(function(index) {
198
+            QSettings.setValue("script/manualMachineSelection", index);
199
+        });
200
+		sampleGreenUnit = findChildObject(this, 'sampleGreenUnit');
201
+		sampleGreenUnit.addItem("g");
202
+		sampleGreenUnit.addItem("Kg");
203
+		sampleGreenUnit.addItem("oz");
204
+		sampleGreenUnit.addItem("lb");
205
+		sampleGreenUnit.currentIndex = (QSettings.value("script/manual_unit", sampleGreenUnit.findText("lb")));
206
+		productionGreenUnit = findChildObject(this, 'productionGreenUnit');
207
+		productionGreenUnit.addItem("g");
208
+		productionGreenUnit.addItem("Kg");
209
+		productionGreenUnit.addItem("oz");
210
+		productionGreenUnit.addItem("lb");
211
+		productionGreenUnit.currentIndex = (QSettings.value("script/manual_unit", productionGreenUnit.findText("lb")));
212
+		sampleGreenUnit['currentIndexChanged(int)'].connect(function(greenUnitIndex) {
213
+			QSettings.setValue("script/manual_unit", greenUnitIndex);
214
+			productionGreenUnit.setCurrentIndex(greenUnitIndex);
215
+		});
216
+		productionGreenUnit['currentIndexChanged(int)'].connect(function(greenUnitIndex) {
217
+			QSettings.setValue("script/manual_unit", greenUnitIndex);
218
+			sampleGreenUnit.setCurrentIndex(greenUnitIndex);
219
+		});
220
+		timeincrement = findChildObject(this, 'timeincrement');
221
+		currenttime = findChildObject(this, 'currenttime');
222
+		currenttemperature = findChildObject(this, 'currenttemperature');
223
+		currentnote = findChildObject(this, 'currentnote');
224
+		addmeasurement = findChildObject(this, 'addmeasurement');
225
+		addmeasurement.clicked.connect(function() {
226
+			pluginContext.newMeasurement(new Measurement(Number(currenttemperature.text), currenttime.time), 1);
227
+			if(currentnote.text.length > 0) {
228
+				pluginContext.table.newAnnotation(currentnote.text, 1, 2);
229
+			}
230
+			currentnote.text = "";
231
+			var t = QTime();
232
+			t = t.fromString(currenttime.time, "hh:mm:ss");
233
+			t = t.addSecs(30);
234
+			currenttime.time = t;
235
+			currenttemperature.text = "";
236
+		});
237
+		currenttemperature.returnPressed.connect(addmeasurement.clicked);
238
+		currentnote.returnPressed.connect(addmeasurement.clicked);
239
+		var v1 = findChildObject(this, 'ms');
240
+        v1.triggered.connect(pluginContext.table.LOD_ms);
241
+        var v2 = findChildObject(this, '1s');
242
+        v2.triggered.connect(pluginContext.table.LOD_1s);
243
+        var v3 = findChildObject(this, '5s');
244
+        v3.triggered.connect(pluginContext.table.LOD_5s);
245
+        var v4 = findChildObject(this, '10s');
246
+        v4.triggered.connect(pluginContext.table.LOD_10s);
247
+        var v5 = findChildObject(this, '15s');
248
+        v5.triggered.connect(pluginContext.table.LOD_15s);
249
+        var v6 = findChildObject(this, '30s');
250
+        v6.triggered.connect(pluginContext.table.LOD_30s);
251
+        var v7 = findChildObject(this, '1m');
252
+        v7.triggered.connect(pluginContext.table.LOD_1m);
253
+		var clear = findChildObject(this, 'clear');
254
+        clear.triggered.connect(pluginContext.table.clear);
255
+        clear.triggered.connect(pluginContext.graph.clear);
256
+		clear.triggered.connect(function() {
257
+			currenttime.time = QTime(0, 0, 0, 0);
258
+			currenttemperature.text = "";
259
+			currentnote.text = "";
260
+		});
261
+		var sampleGreenName = findChildObject(this, 'sampleGreenName');
262
+		var sampleGreenWeight = findChildObject(this, 'sampleGreenWeight');
263
+		var productionGreenTable = findChildObject(this, 'productionGreenTable');
264
+		var greenModel = productionGreenTable.model();
265
+		var greenTotal = 0.0;
266
+		var updateGreenTable = function() {
267
+			var deleteRow = -1;
268
+			while((deleteRow = productionGreenTable.findData("delete", 0)) > -1) {
269
+				if(productionGreenTable.data(deleteRow, 0, 0) == "Delete") {
270
+					productionGreenTable.removeRow(productionGreenTable.findData("delete", 0));
271
+				} else {
272
+					break;
273
+				}
274
+			}
275
+			greenTotal = productionGreenTable.columnSum(1, 0);
276
+			productionGreenTable.resizeColumnToContents(0);
277
+		};
278
+		greenModel.dataChanged.connect(updateGreenTable);
279
+		var validateInputs = function() {
280
+			if(batchType.currentIndex == 0) {
281
+				/* Sample batch */
282
+				if(sampleGreenName.text.length == 0) {
283
+					tabs.setCurrentIndex(0);
284
+					displayError(TTR("manualLogEntry", "Data Entry Error"),
285
+					TTR("manualLogEntry", "Please enter a green coffee name."));
286
+					return false;
287
+				}
288
+				if(Number(sampleGreenWeight.text) <= 0 || isNaN(sampleGreenWeight.text)) {
289
+					tabs.setCurrentIndex(0);
290
+					displayError(TTR("manualLogEntry", "Data Entry Error"),
291
+					TTR("manualLogEntry", "Green coffee weight must be a number greater than 0."));
292
+					return false;
293
+				}
294
+			} else {
295
+				/* Production batch */
296
+				var itemArray = productionGreenTable.columnArray(0, 32).split("\\s*,\\s*");
297
+				var weightArray = productionGreenTable.columnArray(1, 0).split("\\s*,\\s*");
298
+				if((itemArray.length != weightArray.length) || (itemArray.length == 0)) {
299
+					tabs.setCurrentIndex(0);
300
+					displayError(TTR("manualLogEntry", "Data Entry Error"),
301
+					TTR("manualLogEntry", "Please check that at least one green coffee has been selected and each green coffee has a valid weight"));
302
+					return false;
303
+				}
304
+				if(Number(greenTotal) <= 0) {
305
+					tabs.setCurrentIndex(0);
306
+					displayError(TTR("manualLogEntry", "DataEntryError"),
307
+					TTR("manualLogEntry", "Total green coffee weight must be a number greater than 0."));
308
+					return false;
309
+				}
310
+				if(roastedItem.currentIndex == 0) {
311
+					tabs.setCurrentIndex(0);
312
+					displayError(TTR("manualLogEntry", "DataEntryError"),
313
+					TTR("manualLogEntry", "Please select a roasted coffee item."));
314
+					return false;
315
+				}
316
+			}
317
+			return true;
318
+		};
319
+		var roastDataExists = function() {
320
+			return (pluginContext.table.rowCount() > 0);
321
+		}
322
+		var roastTime = findChildObject(this, 'roastTime');
323
+		var attributes = findChildObject(this, 'attributes');
324
+		var sampleGreenArrivalDate = findChildObject(this, 'sampleGreenArrivalDate');
325
+		var convertToPounds = function(w, u) {
326
+			switch(u) {
327
+				case "g":
328
+					return w * 0.0022;
329
+				case "oz":
330
+					return w * 0.0625;
331
+				case "Kg":
332
+					return w * 2.2;
333
+			}
334
+			return w;
335
+		};
336
+		var roastedWeight = findChildObject(this, 'roastedWeight');
337
+		var notes = findChildObject(this, 'notes');
338
+		var roastDuration = findChildObject(this, 'roastDuration');
339
+		var doSubmit = function() {
340
+			var fileID = -1;
341
+			var query = new QSqlQuery();
342
+			if(roastDataExists()) {
343
+				var buffer = new QBuffer;
344
+				buffer.open(3);
345
+				pluginContext.table.saveXML(buffer);
346
+				var q = "INSERT INTO files (id, name, type, note, file) VALUES (default, :name, 'profile', NULL, :data) RETURNING id";
347
+				query.prepare(q);
348
+				query.bind(":name", roastTime.text + " Manual Entry");
349
+				query.bindDeviceData(":data", buffer);
350
+				query.exec();
351
+				query.next();
352
+				fileID = Number(query.value(0));
353
+			}
354
+			var rootIndex = machineModel.index(machineSelector.currentIndex, 0);
355
+			var selectedRoasterName = machineModel.data(rootIndex, 0);
356
+			var machineReference = machineModel.referenceElement(machineModel.data(rootIndex, 32));
357
+			var selectedRoasterID = machineReference.databaseid;
358
+			query.exec("SELECT 1 FROM machine WHERE id = " + selectedRoasterID);
359
+			if(!query.next()) {
360
+				query.prepare("INSERT INTO machine (id, name) VALUES (:id, :name)");
361
+				query.bind(":id", selectedRoasterID);
362
+				query.bind(":name", selectedRoasterName);
363
+				query.exec();
364
+			}
365
+			if(batchType.currentIndex == 0) {
366
+				/* Sample roast */
367
+				var attnames = sqlToArray(attributes.columnArray(0, 0));
368
+				for(var i = 0; i < attnames.length; i++) {
369
+					var attname = attnames[i];
370
+					if(attname[0] == '{') {
371
+						attname = attname.substr(1);
372
+					}
373
+					if(attname[0] == ' ') {
374
+						attname = attname.substr(1);
375
+					}
376
+					if(attname[attname.length - 1] == '}') {
377
+						attname = attname.substr(0, attname.length - 1);
378
+					}
379
+					if(attname.length == 0) {
380
+						break;
381
+					}
382
+					query.prepare("SELECT id FROM item_attributes WHERE name = :name");
383
+					query.bind(":name", attname);
384
+					query.exec();
385
+					if(query.next()) {
386
+						attributes.setData(i, 0, query.value(0), 32);
387
+					} else {
388
+						query.prepare("INSERT INTO item_attributes(id, name) VALUES (DEFAULT, :name) RETURNING id");
389
+						query.bind(":name", attname);
390
+						query.exec();
391
+						query.next();
392
+						attributes.setData(i, 0, query.value(0), 32);
393
+					}
394
+				}
395
+				query.prepare("INSERT INTO coffee_sample_items(id, name, reference, unit, quantity, category, arrival, vendor, attribute_ids, attribute_values) VALUES (DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Green Sample', :arrival, :vendor, :attrids, :attrvals) RETURNING id");
396
+				query.bind(":name", sampleGreenName.text);
397
+				query.bind(":arrival", sampleGreenArrival.date);
398
+				query.bind(":attrids", attributes.bindableColumnArray(0, 32));
399
+				query.bind(":attrvals", attributes.bindableQuotedColumnArray(1, 0));
400
+				query.exec();
401
+				query.next();
402
+				var greenId = query.value(0);
403
+				query.prepare("INSERT INTO items (id, name, reference, unit, quantity, category) VALUES (DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Roasted Sample') RETURNING id");
404
+				query.bind(":name", sampleGreenName.text + " Roasted Sample");
405
+				query.exec();
406
+				query.next();
407
+				var roastedId = query.value(0);
408
+				query.prepare("INSERT INTO roasting_log (time, unroasted_id, unroasted_quantity, unroasted_total_quantity, roasted_id, roasted_quantity, transaction_type, annotation, machine, duration, approval, humidity, barometric, indoor_air, outdoor_air, files) VALUES (:time, :unroastedids, :greens, :green, :roastedid, :roasted, 'SAMPLEROAST', :note, :machine, :duration, TRUE, NULL, NULL, NULL, NULL, :files)");
409
+				query.bind(":time", roastTime.text);
410
+				query.bind(":unroastedids", "{" + greenId + "}");
411
+				query.bind(":greens", "{" + convertToPounds(parseFloat(sampleGreenWeight.text), sampleGreenUnit.currentText) + "}");
412
+				query.bind(":green", convertToPounds(parseFloat(sampleGreenWeight.text), sampleGreenUnit.currentText));
413
+				query.bind("roastedid", Number(roastedId));
414
+				query.bind("roasted", convertToPounds(parseFloat(roastedWeight.text), sampleGreenUnit.currentText));
415
+				query.bind(":note", notes.plainText);
416
+				query.bind(":machine", Number(selectedRoasterID));
417
+				query.bind(":duration", roastDuration.text);
418
+				query.bind(":files", "{" + fileID + "}");
419
+				query.exec();
420
+			} else {
421
+				/* Production roast */
422
+			}
423
+			query = query.invalidate();
424
+			window.close();
425
+		}
426
+		var submit = findChildObject(this, 'submit');
427
+		submit.clicked.connect(function() {
428
+			if(validateInputs()) {
429
+				doSubmit();
430
+			}
431
+		});
432
+	]]>
433
+	</program>
434
+</window>

+ 9
- 0
config/Windows/navigation.xml View File

13
                 <button name="Roast Coffee" id="roast" type="push" />
13
                 <button name="Roast Coffee" id="roast" type="push" />
14
             </column>
14
             </column>
15
         </row>
15
         </row>
16
+		<row>
17
+			<column>
18
+				<button name="Manual Roasting Log Entry" id="manual" type="push" />
19
+			</column>
20
+		</row>
16
         <row>
21
         <row>
17
             <column>
22
             <column>
18
                 <button name="Purchase Green Coffee" id="green" type="push" />
23
                 <button name="Purchase Green Coffee" id="green" type="push" />
87
             QSettings.setValue("database/user", "");
92
             QSettings.setValue("database/user", "");
88
             QSettings.setValue("database/password", "");
93
             QSettings.setValue("database/password", "");
89
         });
94
         });
95
+		var manual = findChildObject(this, 'manual');
96
+		manual.clicked.connect(function() {
97
+			createWindow("manualLogEntry");
98
+		});
90
         var profilehistory = findChildObject(this, 'profilehistory');
99
         var profilehistory = findChildObject(this, 'profilehistory');
91
         profilehistory.clicked.connect(function() {
100
         profilehistory.clicked.connect(function() {
92
                 createWindow("profilehistory");
101
                 createWindow("profilehistory");

+ 79
- 1
config/Windows/newbatch.xml View File

1
 <window id="batchWindow">
1
 <window id="batchWindow">
2
+	<menu name="File">
3
+		<item id="print" shortcut="Ctrl+P">Print...</item>
4
+	</menu>
2
     <menu name="Batch">
5
     <menu name="Batch">
3
         <item id="new" shortcut="Ctrl+N">New Batch...</item>
6
         <item id="new" shortcut="Ctrl+N">New Batch...</item>
4
     </menu>
7
     </menu>
81
             </layout>
84
             </layout>
82
             <stretch />
85
             <stretch />
83
         </layout>
86
         </layout>
87
+		<layout type="vertical">
88
+			<webview id="batchTag" />
89
+			<layout type="horizontal">
90
+				<printerselector id="printerlist" />
91
+				<button name="Print" id="printbutton" type="push" />
92
+			</layout>
93
+		</layout>
84
     </layout>
94
     </layout>
85
     <program>
95
     <program>
86
         <![CDATA[
96
         <![CDATA[
111
             roastwt.maximumWidth = 80;
121
             roastwt.maximumWidth = 80;
112
             var scalesLayout = findChildObject(this, 'scales');
122
             var scalesLayout = findChildObject(this, 'scales');
113
             scalesLayout.spacing = 10;
123
             scalesLayout.spacing = 10;
124
+			var batchTag = findChildObject(this, 'batchTag');
114
             if(navigationwindow.loggingWindow.scales.length > 0) {
125
             if(navigationwindow.loggingWindow.scales.length > 0) {
115
                 for(var i = 0; i < navigationwindow.loggingWindow.scales.length; i++) {
126
                 for(var i = 0; i < navigationwindow.loggingWindow.scales.length; i++) {
116
                     var scale = navigationwindow.loggingWindow.scales[i];
127
                     var scale = navigationwindow.loggingWindow.scales[i];
382
                     lossspec.text = "";
393
                     lossspec.text = "";
383
                     specnotes.plainText = "";
394
                     specnotes.plainText = "";
384
                 }
395
                 }
385
-                roastestimate.text = "";
386
                 query = query.invalidate();
396
                 query = query.invalidate();
397
+				drawTag();
387
             });
398
             });
388
             var validateCapacity = function() {
399
             var validateCapacity = function() {
389
                 if(checkCapacity == "true") {
400
                 if(checkCapacity == "true") {
559
                 filenofield.text = query.value(0);
570
                 filenofield.text = query.value(0);
560
                 var file = new QFile(batch.tempData);
571
                 var file = new QFile(batch.tempData);
561
                 file.remove();
572
                 file.remove();
573
+				drawTag();
562
             }
574
             }
563
             var doSubmit = function() {
575
             var doSubmit = function() {
564
                 checkQuery = new QSqlQuery();
576
                 checkQuery = new QSqlQuery();
619
                 batch.windowModified = false;
631
                 batch.windowModified = false;
620
                 batch.close();
632
                 batch.close();
621
             }
633
             }
634
+			function drawTag() {
635
+				var buffer = new QBuffer;
636
+                buffer.open(3);
637
+                var output = new XmlWriter(buffer);
638
+                output.writeStartDocument("1.0");
639
+                output.writeDTD('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg.dtd">');
640
+                output.writeStartElement("html");
641
+                output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
642
+				output.writeStartElement("head");
643
+				var styleFile = new QFile(QSettings.value("config") + "/Scripts/batchtag.css");
644
+				styleFile.open(1);
645
+				output.writeTextElement("style", styleFile.readToString());
646
+				styleFile.close();
647
+				output.writeStartElement("script");
648
+				scriptFile = new QFile(QSettings.value("config") + "/Scripts/qrcode.js");
649
+				scriptFile.open(1);
650
+				output.writeCDATA(scriptFile.readToString());
651
+				scriptFile.close();
652
+				output.writeEndElement();
653
+				output.writeEndElement();
654
+                output.writeStartElement("body");
655
+				output.writeStartElement("h1");
656
+				output.writeCharacters(roasted.currentText);
657
+				output.writeEndElement();
658
+				output.writeTextElement("span", "Roasted at: " + timefield.text);
659
+				output.writeTextElement("span", "On machine: " + machine.text);
660
+				output.writeTextElement("span", "Batch file: " + filenofield.text);
661
+				output.writeStartElement("div");
662
+				output.writeAttribute("id", "container");
663
+				output.writeEndElement();
664
+				output.writeStartElement("script");
665
+				var tag = {g: "Typica", m: Number(selectedRoasterID), v: 1};
666
+				if(timefield.text.length > 0) {
667
+					tag.t = timefield.text;
668
+				}
669
+				if(filenofield.text.length > 0) {
670
+					tag.f = Number(filenofield.text);
671
+				}
672
+				var scriptData = 'var width = document.getElementById("container").offsetWidth;';
673
+				scriptData += 'var qrcode = new QRCode({content: \'';
674
+				scriptData += JSON.stringify(tag);
675
+				scriptData += '\', width: width, height: width});';
676
+				scriptData += 'var svg = qrcode.svg();';
677
+				scriptData += 'document.getElementById("container").innerHTML = svg;';
678
+				output.writeCDATA(scriptData);
679
+				output.writeEndElement();
680
+				output.writeEndElement();
681
+				output.writeEndElement();
682
+				output.writeEndDocument();
683
+                batchTag.setContent(buffer);
684
+                buffer.close();
685
+			};
686
+			drawTag();
687
+			var printMenu = findChildObject(this, 'print');
688
+            printMenu.triggered.connect(function() {
689
+                batchTag.print();
690
+            });
691
+			var printers = findChildObject(this, 'printerlist');
692
+			printers.currentIndex = printers.findText(QSettings.value("script/batchtagprinter"));
693
+			printers['currentIndexChanged(int)'].connect(function() {
694
+                QSettings.setValue("script/batchtagprinter", printers.currentText);
695
+            });
696
+			var printbutton = findChildObject(this, 'printbutton');
697
+			printbutton.clicked.connect(function() {
698
+				batchTag.print(printers.currentText);
699
+			});
622
         ]]>
700
         ]]>
623
     </program>
701
     </program>
624
 </window>
702
 </window>

+ 33
- 33
config/Windows/newsamplebatch.xml View File

132
 			newMenu.triggered.connect(function() {
132
 			newMenu.triggered.connect(function() {
133
 				createWindow("sampleRoastingBatch");
133
 				createWindow("sampleRoastingBatch");
134
 			});
134
 			});
135
-                        this.endBatch = function() {};
135
+            this.endBatch = function() {};
136
 			var batch = this;
136
 			var batch = this;
137
 			batch.submitButton = submit;
137
 			batch.submitButton = submit;
138
 			var name = findChildObject(this, 'name');
138
 			var name = findChildObject(this, 'name');
180
 			stop.clicked.connect(function() {
180
 			stop.clicked.connect(function() {
181
 				submit.setEnabled(true);
181
 				submit.setEnabled(true);
182
 			});
182
 			});
183
-                        var validateCapacity = function() {
184
-                            if(checkCapacity == "true") {
185
-                                if(convertToPounds(parseFloat(green.text), GunitBox.currentText) > poundsCapacity) {
186
-                                    return false;
187
-                                }
188
-                            }
189
-                            return true;
190
-                        }
191
-                        roastButton.clicked.connect(function() {
192
-                            var proceed = false;
193
-                            if(validateCapacity()) {
194
-                                proceed = true;
195
-                            } else {
196
-                                proceed = displayWarning(TTR("sampleRoastingBatch", "Suspicious Input"),
183
+            var validateCapacity = function() {
184
+                if(checkCapacity == "true") {
185
+                    if(convertToPounds(parseFloat(green.text), GunitBox.currentText) > poundsCapacity) {
186
+                        return false;
187
+                    }
188
+                }
189
+                return true;
190
+            }
191
+            roastButton.clicked.connect(function() {
192
+                var proceed = false;
193
+                if(validateCapacity()) {
194
+                    proceed = true;
195
+                } else {
196
+                    proceed = displayWarning(TTR("sampleRoastingBatch", "Suspicious Input"),
197
                                 TTR("sampleRoastingBatch", "Entered green coffee weight exceeds maximum batch size. Continue?"));
197
                                 TTR("sampleRoastingBatch", "Entered green coffee weight exceeds maximum batch size. Continue?"));
198
-                            }
199
-                            if(proceed) {
200
-                                doRoast();
201
-                            }
202
-                        });
203
-                        var doRoast = function() {
198
+                }
199
+                if(proceed) {
200
+                    doRoast();
201
+                }
202
+                });
203
+                var doRoast = function() {
204
 				var lc = 1;
204
 				var lc = 1;
205
 				currentBatchInfo = batch;
205
 				currentBatchInfo = batch;
206
 				query = new QSqlQuery();
206
 				query = new QSqlQuery();
271
 			var vendor = findChildObject(this, 'vendor');
271
 			var vendor = findChildObject(this, 'vendor');
272
 			var attributes = findChildObject(this, 'attributes');
272
 			var attributes = findChildObject(this, 'attributes');
273
 			var target = findChildObject(this, 'target');
273
 			var target = findChildObject(this, 'target');
274
-                        submit.clicked.connect(function() {
275
-                            var proceed = false;
276
-                            if(validateCapacity()) {
277
-                                proceed = true;
278
-                            } else {
279
-                                proceed = displayWarning(TTR("sampleRoastingBatch", "Suspicious Input"),
274
+            submit.clicked.connect(function() {
275
+                var proceed = false;
276
+                if(validateCapacity()) {
277
+                    proceed = true;
278
+                } else {
279
+                    proceed = displayWarning(TTR("sampleRoastingBatch", "Suspicious Input"),
280
                                 TTR("sampleRoastingBatch", "Entered green coffee weight exceeds maximum batch size. Continue?"));
280
                                 TTR("sampleRoastingBatch", "Entered green coffee weight exceeds maximum batch size. Continue?"));
281
-                            }
282
-                            if(proceed) {
283
-                                doSubmit();
284
-                            }
285
-                        });
286
-                        var doSubmit = function() {
281
+                }
282
+                if(proceed) {
283
+                    doSubmit();
284
+                }
285
+            });
286
+            var doSubmit = function() {
287
 				query = new QSqlQuery();
287
 				query = new QSqlQuery();
288
 				query.prepare("INSERT INTO files (id, name, type, note, file) VALUES(DEFAULT, :name, 'profile', NULL, :data) RETURNING id");
288
 				query.prepare("INSERT INTO files (id, name, type, note, file) VALUES(DEFAULT, :name, 'profile', NULL, :data) RETURNING id");
289
 				query.bind(":name", timefield.text + " " + name.text + " " + profileName.currentText);
289
 				query.bind(":name", timefield.text + " " + name.text + " " + profileName.currentText);

+ 786
- 809
config/Windows/productionroaster.xml
File diff suppressed because it is too large
View File


+ 1
- 0
config/config.xml View File

33
     <include src="Windows/newsamplebatch.xml" />
33
     <include src="Windows/newsamplebatch.xml" />
34
     <include src="Windows/editreminder.xml" />
34
     <include src="Windows/editreminder.xml" />
35
     <include src="Windows/roastspec.xml" />
35
     <include src="Windows/roastspec.xml" />
36
+	<include src="Windows/manuallogentry.xml" />
36
     <program>
37
     <program>
37
         <![CDATA[
38
         <![CDATA[
38
             Windows = new Object();
39
             Windows = new Object();

+ 4
- 2
src/Typica.pro View File

26
     scale.h \
26
     scale.h \
27
     draglabel.h \
27
     draglabel.h \
28
     daterangeselector.h \
28
     daterangeselector.h \
29
-    licensewindow.h
29
+    licensewindow.h \
30
+    printerselector.h
30
 SOURCES += typica.cpp \
31
 SOURCES += typica.cpp \
31
     helpmenu.cpp \
32
     helpmenu.cpp \
32
     abouttypica.cpp \
33
     abouttypica.cpp \
36
     scale.cpp \
37
     scale.cpp \
37
     draglabel.cpp \
38
     draglabel.cpp \
38
     daterangeselector.cpp \
39
     daterangeselector.cpp \
39
-    licensewindow.cpp
40
+    licensewindow.cpp \
41
+    printerselector.cpp
40
 
42
 
41
 RESOURCES += \
43
 RESOURCES += \
42
     resources.qrc
44
     resources.qrc

+ 5
- 5
src/abouttypica.cpp View File

1
-/*275:*/
1
+/*287:*/
2
 #line 33 "./abouttypica.w"
2
 #line 33 "./abouttypica.w"
3
 
3
 
4
 #include "abouttypica.h"
4
 #include "abouttypica.h"
5
 
5
 
6
-/*276:*/
6
+/*288:*/
7
 #line 42 "./abouttypica.w"
7
 #line 42 "./abouttypica.w"
8
 
8
 
9
 AboutTypica::AboutTypica():QMainWindow(NULL)
9
 AboutTypica::AboutTypica():QMainWindow(NULL)
17
 setCentralWidget(banner);
17
 setCentralWidget(banner);
18
 }
18
 }
19
 
19
 
20
-#line 6595 "./typica.w"
20
+#line 6769 "./typica.w"
21
 
21
 
22
-/*:276*/
22
+/*:288*/
23
 #line 36 "./abouttypica.w"
23
 #line 36 "./abouttypica.w"
24
 
24
 
25
 
25
 
26
-/*:275*/
26
+/*:287*/

+ 2
- 2
src/abouttypica.h View File

1
-/*274:*/
1
+/*286:*/
2
 #line 14 "./abouttypica.w"
2
 #line 14 "./abouttypica.w"
3
 
3
 
4
 #include <QMainWindow> 
4
 #include <QMainWindow> 
17
 
17
 
18
 #endif
18
 #endif
19
 
19
 
20
-/*:274*/
20
+/*:286*/

+ 19
- 19
src/daterangeselector.cpp View File

1
-/*666:*/
1
+/*694:*/
2
 #line 70 "./daterangeselector.w"
2
 #line 70 "./daterangeselector.w"
3
 
3
 
4
 #include <QCalendarWidget> 
4
 #include <QCalendarWidget> 
11
 
11
 
12
 #include "daterangeselector.h"
12
 #include "daterangeselector.h"
13
 
13
 
14
-/*668:*/
14
+/*696:*/
15
 #line 117 "./daterangeselector.w"
15
 #line 117 "./daterangeselector.w"
16
 
16
 
17
 CustomDateRangePopup::CustomDateRangePopup(QWidget*parent):
17
 CustomDateRangePopup::CustomDateRangePopup(QWidget*parent):
55
 setLayout(outerLayout);
55
 setLayout(outerLayout);
56
 }
56
 }
57
 
57
 
58
-/*:668*//*669:*/
58
+/*:696*//*697:*/
59
 #line 165 "./daterangeselector.w"
59
 #line 165 "./daterangeselector.w"
60
 
60
 
61
 void CustomDateRangePopup::hideEvent(QHideEvent*)
61
 void CustomDateRangePopup::hideEvent(QHideEvent*)
63
 emit hidingPopup();
63
 emit hidingPopup();
64
 }
64
 }
65
 
65
 
66
-/*:669*//*670:*/
66
+/*:697*//*698:*/
67
 #line 174 "./daterangeselector.w"
67
 #line 174 "./daterangeselector.w"
68
 
68
 
69
 void CustomDateRangePopup::applyRange()
69
 void CustomDateRangePopup::applyRange()
78
 hide();
78
 hide();
79
 }
79
 }
80
 
80
 
81
-/*:670*//*671:*/
81
+/*:698*//*699:*/
82
 #line 191 "./daterangeselector.w"
82
 #line 191 "./daterangeselector.w"
83
 
83
 
84
 void CustomDateRangePopup::validateRange()
84
 void CustomDateRangePopup::validateRange()
93
 }
93
 }
94
 }
94
 }
95
 
95
 
96
-/*:671*/
96
+/*:699*/
97
 #line 81 "./daterangeselector.w"
97
 #line 81 "./daterangeselector.w"
98
 
98
 
99
-/*672:*/
99
+/*700:*/
100
 #line 209 "./daterangeselector.w"
100
 #line 209 "./daterangeselector.w"
101
 
101
 
102
 DateRangeSelector::DateRangeSelector(QWidget*parent):
102
 DateRangeSelector::DateRangeSelector(QWidget*parent):
108
 QDate currentDate= QDate::currentDate();
108
 QDate currentDate= QDate::currentDate();
109
 
109
 
110
 QHBoxLayout*layout= new QHBoxLayout;
110
 QHBoxLayout*layout= new QHBoxLayout;
111
-/*673:*/
111
+/*701:*/
112
 #line 238 "./daterangeselector.w"
112
 #line 238 "./daterangeselector.w"
113
 
113
 
114
 quickSelector->addItem("Yesterday",QVariant(QStringList()<<
114
 quickSelector->addItem("Yesterday",QVariant(QStringList()<<
188
 quickSelector->addItem("Lifetime");
188
 quickSelector->addItem("Lifetime");
189
 quickSelector->addItem("Custom");
189
 quickSelector->addItem("Custom");
190
 
190
 
191
-/*:673*/
191
+/*:701*/
192
 #line 219 "./daterangeselector.w"
192
 #line 219 "./daterangeselector.w"
193
 
193
 
194
 QToolButton*customButton= new QToolButton;
194
 QToolButton*customButton= new QToolButton;
201
 connect(customButton,SIGNAL(clicked()),this,SLOT(toggleCustom()));
201
 connect(customButton,SIGNAL(clicked()),this,SLOT(toggleCustom()));
202
 }
202
 }
203
 
203
 
204
-/*:672*//*674:*/
204
+/*:700*//*702:*/
205
 #line 321 "./daterangeselector.w"
205
 #line 321 "./daterangeselector.w"
206
 
206
 
207
 void DateRangeSelector::updateRange(int index)
207
 void DateRangeSelector::updateRange(int index)
217
 }
217
 }
218
 }
218
 }
219
 
219
 
220
-/*:674*//*675:*/
220
+/*:702*//*703:*/
221
 #line 338 "./daterangeselector.w"
221
 #line 338 "./daterangeselector.w"
222
 
222
 
223
 void DateRangeSelector::popupHidden()
223
 void DateRangeSelector::popupHidden()
227
 quickSelector->setCurrentIndex(lastIndex);
227
 quickSelector->setCurrentIndex(lastIndex);
228
 }
228
 }
229
 
229
 
230
-/*:675*//*676:*/
230
+/*:703*//*704:*/
231
 #line 349 "./daterangeselector.w"
231
 #line 349 "./daterangeselector.w"
232
 
232
 
233
 void DateRangeSelector::setCustomRange(QVariant range)
233
 void DateRangeSelector::setCustomRange(QVariant range)
238
 quickSelector->setCurrentIndex(lastIndex);
238
 quickSelector->setCurrentIndex(lastIndex);
239
 }
239
 }
240
 
240
 
241
-/*:676*//*677:*/
241
+/*:704*//*705:*/
242
 #line 364 "./daterangeselector.w"
242
 #line 364 "./daterangeselector.w"
243
 
243
 
244
 void DateRangeSelector::toggleCustom()
244
 void DateRangeSelector::toggleCustom()
279
 }
279
 }
280
 }
280
 }
281
 
281
 
282
-/*:677*//*678:*/
282
+/*:705*//*706:*/
283
 #line 406 "./daterangeselector.w"
283
 #line 406 "./daterangeselector.w"
284
 
284
 
285
 QVariant DateRangeSelector::currentRange()
285
 QVariant DateRangeSelector::currentRange()
287
 return quickSelector->itemData(lastIndex);
287
 return quickSelector->itemData(lastIndex);
288
 }
288
 }
289
 
289
 
290
-/*:678*//*679:*/
290
+/*:706*//*707:*/
291
 #line 414 "./daterangeselector.w"
291
 #line 414 "./daterangeselector.w"
292
 
292
 
293
 void DateRangeSelector::setCurrentIndex(int index)
293
 void DateRangeSelector::setCurrentIndex(int index)
300
 return quickSelector->currentIndex();
300
 return quickSelector->currentIndex();
301
 }
301
 }
302
 
302
 
303
-/*:679*//*680:*/
303
+/*:707*//*708:*/
304
 #line 434 "./daterangeselector.w"
304
 #line 434 "./daterangeselector.w"
305
 
305
 
306
 void DateRangeSelector::setLifetimeRange(QString startDate,QString endDate)
306
 void DateRangeSelector::setLifetimeRange(QString startDate,QString endDate)
309
 QVariant(QStringList()<<startDate<<endDate));
309
 QVariant(QStringList()<<startDate<<endDate));
310
 }
310
 }
311
 
311
 
312
-/*:680*//*681:*/
312
+/*:708*//*709:*/
313
 #line 444 "./daterangeselector.w"
313
 #line 444 "./daterangeselector.w"
314
 
314
 
315
 void DateRangeSelector::removeIndex(int index)
315
 void DateRangeSelector::removeIndex(int index)
317
 quickSelector->removeItem(index);
317
 quickSelector->removeItem(index);
318
 }
318
 }
319
 
319
 
320
-/*:681*/
320
+/*:709*/
321
 #line 82 "./daterangeselector.w"
321
 #line 82 "./daterangeselector.w"
322
 
322
 
323
 
323
 
327
 #endif
327
 #endif
328
 #endif
328
 #endif
329
 
329
 
330
-/*:666*/
330
+/*:694*/

+ 4
- 4
src/daterangeselector.h View File

1
-/*665:*/
1
+/*693:*/
2
 #line 30 "./daterangeselector.w"
2
 #line 30 "./daterangeselector.w"
3
 
3
 
4
 
4
 
9
 #ifndef TypicaDateRangeSelectorHeader
9
 #ifndef TypicaDateRangeSelectorHeader
10
 #define TypicaDateRangeSelectorHeader
10
 #define TypicaDateRangeSelectorHeader
11
 
11
 
12
-/*667:*/
12
+/*695:*/
13
 #line 93 "./daterangeselector.w"
13
 #line 93 "./daterangeselector.w"
14
 
14
 
15
 class CustomDateRangePopup:public QWidget
15
 class CustomDateRangePopup:public QWidget
31
 QPushButton*applyButton;
31
 QPushButton*applyButton;
32
 };
32
 };
33
 
33
 
34
-/*:667*/
34
+/*:695*/
35
 #line 39 "./daterangeselector.w"
35
 #line 39 "./daterangeselector.w"
36
 
36
 
37
 
37
 
62
 
62
 
63
 #endif
63
 #endif
64
 
64
 
65
-/*:665*/
65
+/*:693*/

+ 2
- 2
src/draglabel.cpp View File

1
-/*1014:*/
1
+/*1039:*/
2
 #line 33 "./scales.w"
2
 #line 33 "./scales.w"
3
 
3
 
4
 #include "draglabel.h"
4
 #include "draglabel.h"
26
 }
26
 }
27
 }
27
 }
28
 
28
 
29
-/*:1014*/
29
+/*:1039*/

+ 2
- 2
src/draglabel.h View File

1
-/*1013:*/
1
+/*1038:*/
2
 #line 13 "./scales.w"
2
 #line 13 "./scales.w"
3
 
3
 
4
 #ifndef TypicaDragLabelInclude
4
 #ifndef TypicaDragLabelInclude
17
 
17
 
18
 #endif
18
 #endif
19
 
19
 
20
-/*:1013*/
20
+/*:1038*/

+ 5
- 1
src/graphsettings.w View File

62
 @ The constructor sets up the interface and restores any previous values from
62
 @ The constructor sets up the interface and restores any previous values from
63
 settings.
63
 settings.
64
 
64
 
65
+The default grid line position has been updated since version 1.8 to match the
66
+number of grid lines present when viewing the graph in Fahrenheit and to
67
+present a slightly wider range where most measurements are expected.
68
+
65
 @<GraphSettingsWidget implementation@>=
69
 @<GraphSettingsWidget implementation@>=
66
 GraphSettingsRelativeTab::GraphSettingsRelativeTab() : QWidget(NULL),
70
 GraphSettingsRelativeTab::GraphSettingsRelativeTab() : QWidget(NULL),
67
 	colorEdit(new QLineEdit)
71
 	colorEdit(new QLineEdit)
101
 	QHBoxLayout *axisLayout = new QHBoxLayout;
105
 	QHBoxLayout *axisLayout = new QHBoxLayout;
102
 	QLabel *axisLabel = new QLabel(tr("Grid line positions (comma separated):"));
106
 	QLabel *axisLabel = new QLabel(tr("Grid line positions (comma separated):"));
103
 	QLineEdit *axisEdit = new QLineEdit;
107
 	QLineEdit *axisEdit = new QLineEdit;
104
-	axisEdit->setText(settings.value("settings/graph/relative/grid", "-300, -100, -10, 0, 10, 30, 50").toString());
108
+	axisEdit->setText(settings.value("settings/graph/relative/grid", "-300, -100, 0, 30, 65, 100").toString());
105
 	updateAxisSetting(axisEdit->text());
109
 	updateAxisSetting(axisEdit->text());
106
 	connect(axisEdit, SIGNAL(textChanged(QString)), this, SLOT(updateAxisSetting(QString)));
110
 	connect(axisEdit, SIGNAL(textChanged(QString)), this, SLOT(updateAxisSetting(QString)));
107
 	axisLayout->addWidget(axisLabel);
111
 	axisLayout->addWidget(axisLabel);

+ 7
- 7
src/helpmenu.cpp View File

1
-/*204:*/
1
+/*207:*/
2
 #line 36 "./helpmenu.w"
2
 #line 36 "./helpmenu.w"
3
 
3
 
4
 #include "helpmenu.h"
4
 #include "helpmenu.h"
5
 #include "abouttypica.h"
5
 #include "abouttypica.h"
6
 #include "licensewindow.h"
6
 #include "licensewindow.h"
7
 
7
 
8
-/*205:*/
8
+/*208:*/
9
 #line 46 "./helpmenu.w"
9
 #line 46 "./helpmenu.w"
10
 
10
 
11
 HelpMenu::HelpMenu():QMenu()
11
 HelpMenu::HelpMenu():QMenu()
24
 #endif
24
 #endif
25
 }
25
 }
26
 
26
 
27
-/*:205*//*206:*/
27
+/*:208*//*209:*/
28
 #line 66 "./helpmenu.w"
28
 #line 66 "./helpmenu.w"
29
 
29
 
30
 void HelpMenu::displayAboutTypica()
30
 void HelpMenu::displayAboutTypica()
33
 aboutBox->show();
33
 aboutBox->show();
34
 }
34
 }
35
 
35
 
36
-/*:206*//*207:*/
36
+/*:209*//*210:*/
37
 #line 76 "./helpmenu.w"
37
 #line 76 "./helpmenu.w"
38
 
38
 
39
 void HelpMenu::displayLicenseWindow()
39
 void HelpMenu::displayLicenseWindow()
42
 window->show();
42
 window->show();
43
 }
43
 }
44
 
44
 
45
-#line 4775 "./typica.w"
45
+#line 4826 "./typica.w"
46
 
46
 
47
 #line 1 "./licensewindow.w"
47
 #line 1 "./licensewindow.w"
48
-/*:207*/
48
+/*:210*/
49
 #line 41 "./helpmenu.w"
49
 #line 41 "./helpmenu.w"
50
 
50
 
51
 
51
 
52
-/*:204*/
52
+/*:207*/

+ 2
- 2
src/helpmenu.h View File

1
-/*203:*/
1
+/*206:*/
2
 #line 16 "./helpmenu.w"
2
 #line 16 "./helpmenu.w"
3
 
3
 
4
 #include <QMenu> 
4
 #include <QMenu> 
18
 
18
 
19
 #endif
19
 #endif
20
 
20
 
21
-/*:203*/
21
+/*:206*/

+ 10
- 10
src/licensewindow.cpp View File

1
-/*209:*/
1
+/*212:*/
2
 #line 36 "./licensewindow.w"
2
 #line 36 "./licensewindow.w"
3
 
3
 
4
-/*213:*/
4
+/*216:*/
5
 #line 97 "./licensewindow.w"
5
 #line 97 "./licensewindow.w"
6
 
6
 
7
 #include "licensewindow.h"
7
 #include "licensewindow.h"
11
 #include <QVariant> 
11
 #include <QVariant> 
12
 #include <QUrl> 
12
 #include <QUrl> 
13
 
13
 
14
-#line 4777 "./typica.w"
14
+#line 4828 "./typica.w"
15
 
15
 
16
-/*:213*/
16
+/*:216*/
17
 #line 37 "./licensewindow.w"
17
 #line 37 "./licensewindow.w"
18
 
18
 
19
-/*210:*/
19
+/*213:*/
20
 #line 43 "./licensewindow.w"
20
 #line 43 "./licensewindow.w"
21
 
21
 
22
 LicenseWindow::LicenseWindow()
22
 LicenseWindow::LicenseWindow()
25
 QSplitter*split= new QSplitter;
25
 QSplitter*split= new QSplitter;
26
 QListWidget*projects= new QListWidget;
26
 QListWidget*projects= new QListWidget;
27
 
27
 
28
-/*212:*/
28
+/*215:*/
29
 #line 79 "./licensewindow.w"
29
 #line 79 "./licensewindow.w"
30
 
30
 
31
 QListWidgetItem*item= new QListWidgetItem("Typica",projects);
31
 QListWidgetItem*item= new QListWidgetItem("Typica",projects);
43
 item= new QListWidgetItem("Qt",projects);
43
 item= new QListWidgetItem("Qt",projects);
44
 item->setData(Qt::UserRole,QVariant(QUrl("qrc:/resources/html/licenses/qt.html")));
44
 item->setData(Qt::UserRole,QVariant(QUrl("qrc:/resources/html/licenses/qt.html")));
45
 
45
 
46
-/*:212*/
46
+/*:215*/
47
 #line 50 "./licensewindow.w"
47
 #line 50 "./licensewindow.w"
48
 
48
 
49
 connect(projects,SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
49
 connect(projects,SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
54
 setCentralWidget(split);
54
 setCentralWidget(split);
55
 }
55
 }
56
 
56
 
57
-/*:210*//*211:*/
57
+/*:213*//*214:*/
58
 #line 64 "./licensewindow.w"
58
 #line 64 "./licensewindow.w"
59
 
59
 
60
 void LicenseWindow::setWebView(QListWidgetItem*current,QListWidgetItem*)
60
 void LicenseWindow::setWebView(QListWidgetItem*current,QListWidgetItem*)
62
 view->load(current->data(Qt::UserRole).toUrl());
62
 view->load(current->data(Qt::UserRole).toUrl());
63
 }
63
 }
64
 
64
 
65
-/*:211*/
65
+/*:214*/
66
 #line 38 "./licensewindow.w"
66
 #line 38 "./licensewindow.w"
67
 
67
 
68
 
68
 
69
-/*:209*/
69
+/*:212*/

+ 2
- 2
src/licensewindow.h View File

1
-/*208:*/
1
+/*211:*/
2
 #line 13 "./licensewindow.w"
2
 #line 13 "./licensewindow.w"
3
 
3
 
4
 #include <QMainWindow> 
4
 #include <QMainWindow> 
21
 
21
 
22
 #endif
22
 #endif
23
 
23
 
24
-/*:208*/
24
+/*:211*/

+ 103
- 21
src/moc_typica.cpp View File

3096
     }
3096
     }
3097
     return _id;
3097
     return _id;
3098
 }
3098
 }
3099
+static const uint qt_meta_data_PluginAction[] = {
3100
+
3101
+ // content:
3102
+       6,       // revision
3103
+       0,       // classname
3104
+       0,    0, // classinfo
3105
+       1,   14, // methods
3106
+       0,    0, // properties
3107
+       0,    0, // enums/sets
3108
+       0,    0, // constructors
3109
+       0,       // flags
3110
+       0,       // signalCount
3111
+
3112
+ // slots: signature, parameters, type, tag, flags
3113
+      14,   13,   13,   13, 0x08,
3114
+
3115
+       0        // eod
3116
+};
3117
+
3118
+static const char qt_meta_stringdata_PluginAction[] = {
3119
+    "PluginAction\0\0runScript()\0"
3120
+};
3121
+
3122
+void PluginAction::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
3123
+{
3124
+    if (_c == QMetaObject::InvokeMetaMethod) {
3125
+        Q_ASSERT(staticMetaObject.cast(_o));
3126
+        PluginAction *_t = static_cast<PluginAction *>(_o);
3127
+        switch (_id) {
3128
+        case 0: _t->runScript(); break;
3129
+        default: ;
3130
+        }
3131
+    }
3132
+    Q_UNUSED(_a);
3133
+}
3134
+
3135
+const QMetaObjectExtraData PluginAction::staticMetaObjectExtraData = {
3136
+    0,  qt_static_metacall 
3137
+};
3138
+
3139
+const QMetaObject PluginAction::staticMetaObject = {
3140
+    { &QAction::staticMetaObject, qt_meta_stringdata_PluginAction,
3141
+      qt_meta_data_PluginAction, &staticMetaObjectExtraData }
3142
+};
3143
+
3144
+#ifdef Q_NO_DATA_RELOCATION
3145
+const QMetaObject &PluginAction::getStaticMetaObject() { return staticMetaObject; }
3146
+#endif //Q_NO_DATA_RELOCATION
3147
+
3148
+const QMetaObject *PluginAction::metaObject() const
3149
+{
3150
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
3151
+}
3152
+
3153
+void *PluginAction::qt_metacast(const char *_clname)
3154
+{
3155
+    if (!_clname) return 0;
3156
+    if (!strcmp(_clname, qt_meta_stringdata_PluginAction))
3157
+        return static_cast<void*>(const_cast< PluginAction*>(this));
3158
+    return QAction::qt_metacast(_clname);
3159
+}
3160
+
3161
+int PluginAction::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
3162
+{
3163
+    _id = QAction::qt_metacall(_c, _id, _a);
3164
+    if (_id < 0)
3165
+        return _id;
3166
+    if (_c == QMetaObject::InvokeMetaMethod) {
3167
+        if (_id < 1)
3168
+            qt_static_metacall(this, _c, _id, _a);
3169
+        _id -= 1;
3170
+    }
3171
+    return _id;
3172
+}
3099
 static const uint qt_meta_data_FormArray[] = {
3173
 static const uint qt_meta_data_FormArray[] = {
3100
 
3174
 
3101
  // content:
3175
  // content:
6283
        6,       // revision
6357
        6,       // revision
6284
        0,       // classname
6358
        0,       // classname
6285
        0,    0, // classinfo
6359
        0,    0, // classinfo
6286
-       7,   14, // methods
6360
+       9,   14, // methods
6287
        0,    0, // properties
6361
        0,    0, // properties
6288
        0,    0, // enums/sets
6362
        0,    0, // enums/sets
6289
        0,    0, // constructors
6363
        0,    0, // constructors
6294
       10,    9,    9,    9, 0x08,
6368
       10,    9,    9,    9, 0x08,
6295
       28,    9,    9,    9, 0x08,
6369
       28,    9,    9,    9, 0x08,
6296
       38,    9,    9,    9, 0x08,
6370
       38,    9,    9,    9, 0x08,
6371
+      54,    9,    9,    9, 0x08,
6297
 
6372
 
6298
  // methods: signature, parameters, type, tag, flags
6373
  // methods: signature, parameters, type, tag, flags
6299
-      58,    9,   54,    9, 0x02,
6300
-      81,    9,   73,    9, 0x02,
6301
-     104,    9,   73,    9, 0x02,
6302
-     135,    9,  130,    9, 0x02,
6374
+      77,    9,   73,    9, 0x02,
6375
+     100,    9,   92,    9, 0x02,
6376
+     123,    9,   92,    9, 0x02,
6377
+     154,    9,  149,    9, 0x02,
6378
+     175,    9,   92,    9, 0x02,
6303
 
6379
 
6304
        0        // eod
6380
        0        // eod
6305
 };
6381
 };
6306
 
6382
 
6307
 static const char qt_meta_stringdata_ModbusNG[] = {
6383
 static const char qt_meta_stringdata_ModbusNG[] = {
6308
     "ModbusNG\0\0sendNextMessage()\0timeout()\0"
6384
     "ModbusNG\0\0sendNextMessage()\0timeout()\0"
6309
-    "dataAvailable()\0int\0channelCount()\0"
6310
-    "QString\0channelColumnName(int)\0"
6311
-    "channelIndicatorText(int)\0bool\0"
6312
-    "isChannelHidden(int)\0"
6385
+    "dataAvailable()\0rateLimitTimeout()\0"
6386
+    "int\0channelCount()\0QString\0"
6387
+    "channelColumnName(int)\0channelIndicatorText(int)\0"
6388
+    "bool\0isChannelHidden(int)\0channelType(int)\0"
6313
 };
6389
 };
6314
 
6390
 
6315
 void ModbusNG::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
6391
 void ModbusNG::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
6321
         case 0: _t->sendNextMessage(); break;
6397
         case 0: _t->sendNextMessage(); break;
6322
         case 1: _t->timeout(); break;
6398
         case 1: _t->timeout(); break;
6323
         case 2: _t->dataAvailable(); break;
6399
         case 2: _t->dataAvailable(); break;
6324
-        case 3: { int _r = _t->channelCount();
6400
+        case 3: _t->rateLimitTimeout(); break;
6401
+        case 4: { int _r = _t->channelCount();
6325
             if (_a[0]) *reinterpret_cast< int*>(_a[0]) = _r; }  break;
6402
             if (_a[0]) *reinterpret_cast< int*>(_a[0]) = _r; }  break;
6326
-        case 4: { QString _r = _t->channelColumnName((*reinterpret_cast< int(*)>(_a[1])));
6403
+        case 5: { QString _r = _t->channelColumnName((*reinterpret_cast< int(*)>(_a[1])));
6327
             if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = _r; }  break;
6404
             if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = _r; }  break;
6328
-        case 5: { QString _r = _t->channelIndicatorText((*reinterpret_cast< int(*)>(_a[1])));
6405
+        case 6: { QString _r = _t->channelIndicatorText((*reinterpret_cast< int(*)>(_a[1])));
6329
             if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = _r; }  break;
6406
             if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = _r; }  break;
6330
-        case 6: { bool _r = _t->isChannelHidden((*reinterpret_cast< int(*)>(_a[1])));
6407
+        case 7: { bool _r = _t->isChannelHidden((*reinterpret_cast< int(*)>(_a[1])));
6331
             if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = _r; }  break;
6408
             if (_a[0]) *reinterpret_cast< bool*>(_a[0]) = _r; }  break;
6409
+        case 8: { QString _r = _t->channelType((*reinterpret_cast< int(*)>(_a[1])));
6410
+            if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = _r; }  break;
6332
         default: ;
6411
         default: ;
6333
         }
6412
         }
6334
     }
6413
     }
6366
     if (_id < 0)
6445
     if (_id < 0)
6367
         return _id;
6446
         return _id;
6368
     if (_c == QMetaObject::InvokeMetaMethod) {
6447
     if (_c == QMetaObject::InvokeMetaMethod) {
6369
-        if (_id < 7)
6448
+        if (_id < 9)
6370
             qt_static_metacall(this, _c, _id, _a);
6449
             qt_static_metacall(this, _c, _id, _a);
6371
-        _id -= 7;
6450
+        _id -= 9;
6372
     }
6451
     }
6373
     return _id;
6452
     return _id;
6374
 }
6453
 }
7350
        6,       // revision
7429
        6,       // revision
7351
        0,       // classname
7430
        0,       // classname
7352
        0,    0, // classinfo
7431
        0,    0, // classinfo
7353
-       2,   14, // methods
7432
+       3,   14, // methods
7354
        0,    0, // properties
7433
        0,    0, // properties
7355
        0,    0, // enums/sets
7434
        0,    0, // enums/sets
7356
-       1,   24, // constructors
7435
+       1,   29, // constructors
7357
        0,       // flags
7436
        0,       // flags
7358
        0,       // signalCount
7437
        0,       // signalCount
7359
 
7438
 
7360
  // slots: signature, parameters, type, tag, flags
7439
  // slots: signature, parameters, type, tag, flags
7361
       30,   23,   22,   22, 0x08,
7440
       30,   23,   22,   22, 0x08,
7362
       60,   22,   22,   22, 0x08,
7441
       60,   22,   22,   22, 0x08,
7442
+      80,   22,   22,   22, 0x08,
7363
 
7443
 
7364
  // constructors: signature, parameters, type, tag, flags
7444
  // constructors: signature, parameters, type, tag, flags
7365
-      92,   80,   22,   22, 0x0e,
7445
+     106,   94,   22,   22, 0x0e,
7366
 
7446
 
7367
        0        // eod
7447
        0        // eod
7368
 };
7448
 };
7370
 static const char qt_meta_stringdata_TranslationConfWidget[] = {
7450
 static const char qt_meta_stringdata_TranslationConfWidget[] = {
7371
     "TranslationConfWidget\0\0column\0"
7451
     "TranslationConfWidget\0\0column\0"
7372
     "updateMatchingColumn(QString)\0"
7452
     "updateMatchingColumn(QString)\0"
7373
-    "updateTemperature()\0model,index\0"
7453
+    "updateTemperature()\0updateDelay()\0"
7454
+    "model,index\0"
7374
     "TranslationConfWidget(DeviceTreeModel*,QModelIndex)\0"
7455
     "TranslationConfWidget(DeviceTreeModel*,QModelIndex)\0"
7375
 };
7456
 };
7376
 
7457
 
7387
         switch (_id) {
7468
         switch (_id) {
7388
         case 0: _t->updateMatchingColumn((*reinterpret_cast< const QString(*)>(_a[1]))); break;
7469
         case 0: _t->updateMatchingColumn((*reinterpret_cast< const QString(*)>(_a[1]))); break;
7389
         case 1: _t->updateTemperature(); break;
7470
         case 1: _t->updateTemperature(); break;
7471
+        case 2: _t->updateDelay(); break;
7390
         default: ;
7472
         default: ;
7391
         }
7473
         }
7392
     }
7474
     }
7424
     if (_id < 0)
7506
     if (_id < 0)
7425
         return _id;
7507
         return _id;
7426
     if (_c == QMetaObject::InvokeMetaMethod) {
7508
     if (_c == QMetaObject::InvokeMetaMethod) {
7427
-        if (_id < 2)
7509
+        if (_id < 3)
7428
             qt_static_metacall(this, _c, _id, _a);
7510
             qt_static_metacall(this, _c, _id, _a);
7429
-        _id -= 2;
7511
+        _id -= 3;
7430
     }
7512
     }
7431
     return _id;
7513
     return _id;
7432
 }
7514
 }

+ 162
- 0
src/plugins.w View File

1
+@** Simple Plugins.
2
+
3
+\noindent The original motivation for this feature is to provide a simple way
4
+to allow importing data from other data logging applications. The problem is
5
+that there are huge differences in the data formats exported by different
6
+applications, sometimes there are differences that depend on how the other
7
+application was configured which cannot be reliably determined in an automated
8
+fashion, and if a substantial number of import plugins were created, any given
9
+person using Typica would be unlikely to ever use most of them.
10
+
11
+Based on these concerns, I wanted something that would make it easy to create
12
+new import plugins without the need to create a new build of Typica every time,
13
+I wanted it to be relatively easy for people to modify example import plugins
14
+to suit the data they wanted to import, and I wanted it to be easy for people
15
+to hide plugins that they were not required.
16
+
17
+This is handled in a way similar to reports. A new directory is provided with
18
+the \pn{} configuration which contains files with script code. A menu item is
19
+available that will examine the files in that folder to populate its sub-menu.
20
+
21
+@<Process plugin item@>=
22
+QMenu *pluginMenu = new QMenu(menu);
23
+if(itemElement.hasAttribute("id"))
24
+{
25
+	pluginMenu->setObjectName(itemElement.attribute("id"));
26
+}
27
+if(itemElement.hasAttribute("title"))
28
+{
29
+	pluginMenu->setTitle(itemElement.attribute("title"));
30
+}
31
+if(itemElement.hasAttribute("src"))
32
+{
33
+	QSettings settings;
34
+	QString pluginDirectory = QString("%1/%2").
35
+		arg(settings.value("config").toString()).
36
+		arg(itemElement.attribute("src"));
37
+	QDir directory(pluginDirectory);
38
+	directory.setFilter(QDir::Files);
39
+	directory.setSorting(QDir::Name);
40
+	QStringList nameFilter;
41
+	nameFilter << "*.js";
42
+	directory.setNameFilters(nameFilter);
43
+	QFileInfoList pluginFiles = directory.entryInfoList();
44
+	for(int k = 0; k < pluginFiles.size(); k++)
45
+	{
46
+		PluginAction *pa = new PluginAction(pluginFiles.at(k), pluginMenu);
47
+		if(itemElement.hasAttribute("preRun"))
48
+		{
49
+			pa->setPreRun(itemElement.attribute("preRun"));
50
+		}
51
+		if(itemElement.hasAttribute("postRun"))
52
+		{
53
+			pa->setPostRun(itemElement.attribute("postRun"));
54
+		}
55
+		pluginMenu->addAction(pa);
56
+	}
57
+}
58
+menu->addMenu(pluginMenu);
59
+
60
+@ The sub-menu items are a subclass of |QAction| which holds all of the
61
+information needed to respond to its activation.
62
+
63
+@<Class declarations@>=
64
+class PluginAction : public QAction
65
+{
66
+	Q_OBJECT
67
+	Q_PROPERTY(QString preRun READ preRun WRITE setPreRun);
68
+	Q_PROPERTY(QString postRun READ postRun WRITE setPostRun);
69
+	public:
70
+		PluginAction(const QFileInfo &info, QObject *parent);
71
+		QString preRun();
72
+		QString postRun();
73
+	public slots:
74
+		void setPreRun(const QString &script);
75
+		void setPostRun(const QString &script);
76
+	private slots:
77
+		void runScript();
78
+	private:
79
+		QString pluginFile;
80
+		QString preRunScript;
81
+		QString postRunScript;
82
+};
83
+
84
+@ The constructor takes a |QFileInfo| and uses that to extract the path of the
85
+file used to respond to the action activation as well as the text that should
86
+be used in the menu text. It also takes a |QObject*| parent which should be the
87
+|QMenu| the |PluginAction| will be placed in.
88
+
89
+Everything interesting happens in |runScript()| which is called when the action
90
+is triggered.
91
+
92
+@<PluginAction implementation@>=
93
+PluginAction::PluginAction(const QFileInfo &info, QObject *parent) :
94
+	QAction(parent), preRunScript(""), postRunScript("")
95
+{
96
+	pluginFile = info.absoluteFilePath();
97
+	setText(info.baseName());
98
+	connect(this, SIGNAL(triggered()), this, SLOT(runScript()));
99
+}
100
+
101
+void PluginAction::runScript()
102
+{
103
+	QFile file(pluginFile);
104
+	if(file.open(QIODevice::ReadOnly))
105
+	{
106
+		QScriptEngine *engine = AppInstance->engine;
107
+		QScriptContext *context = engine->pushContext();
108
+		if(parent()->dynamicPropertyNames().contains("activationObject"))
109
+		{
110
+			QScriptValue activationObject =
111
+				parent()->property("activationObject").value<QScriptValue>();
112
+			context->setActivationObject(activationObject);
113
+		}
114
+		QString script(file.readAll());
115
+		QScriptValue retval = engine->evaluate(preRunScript + script + postRunScript, pluginFile);
116
+		if(engine->hasUncaughtException())
117
+		{
118
+			qDebug() << "Uncaught exception: " <<
119
+				engine->uncaughtException().toString() <<
120
+				" in " << pluginFile << " line: " <<
121
+				engine->uncaughtExceptionLineNumber();
122
+		}
123
+		engine->popContext();
124
+		file.close();
125
+	}
126
+}
127
+
128
+@ Pre-run and post-run scripts can be set to handle boilerplate that would
129
+otherwise need to be included in all plugins.
130
+
131
+@<PluginAction implementation@>=
132
+QString PluginAction::preRun()
133
+{
134
+	return preRunScript;
135
+}
136
+
137
+QString PluginAction::postRun()
138
+{
139
+	return postRunScript;
140
+}
141
+
142
+void PluginAction::setPreRun(const QString &script)
143
+{
144
+	preRunScript = script;
145
+}
146
+
147
+void PluginAction::setPostRun(const QString &script)
148
+{
149
+	postRunScript = script;
150
+}
151
+
152
+@ In order to get the activation object in this way, we need to allow a
153
+|QScriptValue| to be stored in a |QVariant|.
154
+
155
+@<Class declarations@>=
156
+Q_DECLARE_METATYPE(QScriptValue)
157
+
158
+@ This is added to the list of class implementations.
159
+
160
+@<Class implementations@>=
161
+@<PluginAction implementation@>
162
+

+ 22
- 0
src/printerselector.cpp View File

1
+/*590:*/
2
+#line 45 "./printerselector.w"
3
+
4
+#include "printerselector.h"
5
+
6
+/*591:*/
7
+#line 53 "./printerselector.w"
8
+
9
+PrinterSelector::PrinterSelector():QComboBox(NULL)
10
+{
11
+QList<QPrinterInfo> printers= QPrinterInfo::availablePrinters();
12
+foreach(QPrinterInfo info,printers)
13
+{
14
+addItem(info.printerName());
15
+}
16
+}
17
+
18
+/*:591*/
19
+#line 48 "./printerselector.w"
20
+
21
+
22
+/*:590*/

+ 19
- 0
src/printerselector.h View File

1
+/*588:*/
2
+#line 22 "./printerselector.w"
3
+
4
+#include <QPrinterInfo> 
5
+#include <QComboBox> 
6
+
7
+#ifndef TypicaPrinterSelectorHeader
8
+#define TypicaPrinterSelectorHeader
9
+
10
+class PrinterSelector:public QComboBox
11
+{
12
+Q_OBJECT
13
+public:
14
+PrinterSelector();
15
+};
16
+
17
+#endif
18
+
19
+/*:588*/

+ 116
- 0
src/printerselector.w View File

1
+@* Saved Printers.
2
+
3
+\noindent In most cases it's best to handle printing in a way that is common
4
+across many applications. Put a Print menu option in a File menu, bring up the
5
+platform's standard print dialog, and allow people to take full advantage of
6
+the flexibility this provides.
7
+
8
+In more specialized use cases, however, it may make more sense to provide
9
+faster access to a printer that might not be the default printer for that
10
+computer. The first use in Typica where this makes sense is in printing tags
11
+that can follow the coffee and uniquely identify that batch. Using a full sheet
12
+of paper for this might be excessive and time consuming. Instead, it might make
13
+sense to get a small, inexpensive thermal receipt printer to keep at the
14
+roaster. If this were not the default printer, it would quickly become tedious
15
+to bring up the print dialog and change the selected printer after every batch.
16
+
17
+In cases like this, it would be better to provide a combo box in the window
18
+where a printer can be selected and remembered as the default printer just for
19
+that particular use, and allowing people to print directly to that printer
20
+without going through extra steps.
21
+
22
+@(printerselector.h@>=
23
+#include <QPrinterInfo>
24
+#include <QComboBox>
25
+
26
+#ifndef TypicaPrinterSelectorHeader
27
+#define TypicaPrinterSelectorHeader
28
+
29
+class PrinterSelector : public QComboBox@/
30
+{
31
+	@[Q_OBJECT@]@;
32
+	public:
33
+		PrinterSelector();
34
+};
35
+
36
+#endif
37
+
38
+@ The main file also requires this header.
39
+
40
+@<Header files to include@>=
41
+#include "printerselector.h"
42
+
43
+@ Implementation of this class is in a separate file.
44
+
45
+@(printerselector.cpp@>=
46
+#include "printerselector.h"
47
+
48
+@<PrinterSelector implementation@>@;
49
+
50
+@ The constructor looks at the list of available printers and populates itself
51
+with these.
52
+
53
+@<PrinterSelector implementation@>=
54
+PrinterSelector::PrinterSelector() : QComboBox(NULL)
55
+{
56
+	QList<QPrinterInfo> printers = QPrinterInfo::availablePrinters();
57
+	foreach(QPrinterInfo info, printers)
58
+	{
59
+		addItem(info.printerName());
60
+	}
61
+}
62
+
63
+@ The host environment is informed of this class in the usual way starting with
64
+a constructor function prototype. Another prototype is also needed for adding
65
+this to a layout from XML.
66
+
67
+@<Function prototypes for scripting@>=
68
+QScriptValue constructPrinterSelector(QScriptContext *context,
69
+                                      QScriptEngine *engine);
70
+void addPrinterSelectorToLayout(QDomElement element,
71
+                                QStack<QWidget *> *widgetStack,
72
+                                QStack<QLayout *> *layoutStack);
73
+
74
+@ The engine is informed of this function.
75
+
76
+@<Set up the scripting engine@>=
77
+constructor = engine->newFunction(constructPrinterSelector);
78
+engine->globalObject().setProperty("PrinterSelector", constructor);
79
+
80
+@ There is nothing special about the constructor. If there were additional
81
+properties needed beyond those supplied by |setQComboBoxProperties()| it would
82
+make sense to add another function to the chain for setting script value
83
+properties.
84
+
85
+@<Functions for scripting@>=
86
+QScriptValue constructPrinterSelector(QScriptContext *, QScriptEngine *engine)
87
+{
88
+	QScriptValue object = engine->newQObject(new PrinterSelector);
89
+	setQComboBoxProperties(object, engine);
90
+	return object;
91
+}
92
+
93
+@ It should also be possible to add this to a layout from the XML portion of
94
+the configuration document.
95
+
96
+@<Functions for scripting@>=
97
+void addPrinterSelectorToLayout(QDomElement element, QStack<QWidget *> *,
98
+                                QStack<QLayout *> *layoutStack)
99
+{
100
+	PrinterSelector *selector = new PrinterSelector;
101
+	if(element.hasAttribute("id"))
102
+	{
103
+		selector->setObjectName(element.attribute("id"));
104
+	}
105
+	QBoxLayout *layout = qobject_cast<QBoxLayout *>(layoutStack->top());
106
+	layout->addWidget(selector);
107
+}
108
+
109
+@ This is added in the usual way.
110
+
111
+@<Additional box layout elements@>=
112
+else if(currentElement.tagName() == "printerselector")
113
+{
114
+	addPrinterSelectorToLayout(currentElement, widgetStack, layoutStack);
115
+}
116
+

+ 6
- 6
src/scale.cpp View File

1
-/*1020:*/
1
+/*1045:*/
2
 #line 135 "./scales.w"
2
 #line 135 "./scales.w"
3
 
3
 
4
 #include "scale.h"
4
 #include "scale.h"
10
 connect(this,SIGNAL(readyRead()),this,SLOT(dataAvailable()));
10
 connect(this,SIGNAL(readyRead()),this,SLOT(dataAvailable()));
11
 }
11
 }
12
 
12
 
13
-/*:1020*//*1021:*/
13
+/*:1045*//*1046:*/
14
 #line 153 "./scales.w"
14
 #line 153 "./scales.w"
15
 
15
 
16
 void SerialScale::dataAvailable()
16
 void SerialScale::dataAvailable()
24
 }
24
 }
25
 else
25
 else
26
 {
26
 {
27
-/*1022:*/
27
+/*1047:*/
28
 #line 193 "./scales.w"
28
 #line 193 "./scales.w"
29
 
29
 
30
 QStringList responseParts= QString(responseBuffer.simplified()).split(' ');
30
 QStringList responseParts= QString(responseBuffer.simplified()).split(' ');
53
 }
53
 }
54
 emit newMeasurement(weight,unit);
54
 emit newMeasurement(weight,unit);
55
 
55
 
56
-/*:1022*/
56
+/*:1047*/
57
 #line 165 "./scales.w"
57
 #line 165 "./scales.w"
58
 
58
 
59
 responseBuffer.clear();
59
 responseBuffer.clear();
61
 }
61
 }
62
 }
62
 }
63
 
63
 
64
-/*:1021*//*1023:*/
64
+/*:1046*//*1048:*/
65
 #line 224 "./scales.w"
65
 #line 224 "./scales.w"
66
 
66
 
67
 void SerialScale::tare()
67
 void SerialScale::tare()
96
 }
96
 }
97
 }
97
 }
98
 
98
 
99
-/*:1023*/
99
+/*:1048*/

+ 2
- 2
src/scale.h View File

1
-/*1019:*/
1
+/*1044:*/
2
 #line 103 "./scales.w"
2
 #line 103 "./scales.w"
3
 
3
 
4
 #ifndef TypicaScaleInclude
4
 #ifndef TypicaScaleInclude
29
 
29
 
30
 #endif
30
 #endif
31
 
31
 
32
-/*:1019*/
32
+/*:1044*/

+ 3039
- 2532
src/typica.cpp
File diff suppressed because it is too large
View File


+ 202
- 15
src/typica.w View File

810
 
810
 
811
 @<Function prototypes for scripting@>=
811
 @<Function prototypes for scripting@>=
812
 void setQObjectProperties(QScriptValue value, QScriptEngine *engine);
812
 void setQObjectProperties(QScriptValue value, QScriptEngine *engine);
813
+QScriptValue QObject_setProperty(QScriptContext *context, QScriptEngine *engine);
813
 
814
 
814
-@ As there are no properties that need to be set for this class and as this
815
-class does not inherit any other class, nothing needs to be done in this method.
816
-It will, however, be called by subclasses in case this changes in the future.
815
+@ Attaching properties to a |QScriptValue| that wraps a |QObject| does not
816
+create a dynamic property on the underlying |QObject| by default. This can
817
+cause issues with certain interactions between script and native code. Rather
818
+than change every wrapper, we can instead expose a |setProperty()| method.
817
 
819
 
818
 @<Functions for scripting@>=
820
 @<Functions for scripting@>=
819
-void setQObjectProperties(QScriptValue, QScriptEngine *)
821
+void setQObjectProperties(QScriptValue value, QScriptEngine *engine)
820
 {
822
 {
821
-    /* Nothing needs to be done here. */
823
+    value.setProperty("setProperty", engine->newFunction(QObject_setProperty));
824
+}
825
+
826
+QScriptValue QObject_setProperty(QScriptContext *context, QScriptEngine *)
827
+{
828
+	QObject *self = getself<QObject *>(context);
829
+	self->setProperty(argument<QString>(0, context).toUtf8().constData(),
830
+	                  argument<QVariant>(1, context));
831
+    return QScriptValue();
822
 }
832
 }
823
 
833
 
824
 @ The same can be done for |QPaintDevice| and |QLayoutItem|.
834
 @ The same can be done for |QPaintDevice| and |QLayoutItem|.
1029
 Version 1.6 adds a new property for handling the |windowModified| property
1039
 Version 1.6 adds a new property for handling the |windowModified| property
1030
 such that an appropriate prompt is provided to confirm or cancel close events.
1040
 such that an appropriate prompt is provided to confirm or cancel close events.
1031
 
1041
 
1042
+Version 1.8 adds a new |setupFinished()| slot which is called after the
1043
+initial |show()| at the end of window creation. This emits a |windowReady()|
1044
+signal. Scripts can connect to this signal to perform tasks that must happen
1045
+after the window has fully finished opening. The initial use for this is
1046
+validating that all required configuration has been performed for a given
1047
+window to be useful and, if not, immediately closing that. Without this, a
1048
+call to |close()| in the script is reversed when the function creating the
1049
+window calls |show()|.
1050
+
1032
 @<Class declarations@>=
1051
 @<Class declarations@>=
1033
 class ScriptQMainWindow : public QMainWindow@/
1052
 class ScriptQMainWindow : public QMainWindow@/
1034
 {@t\1@>@/
1053
 {@t\1@>@/
1042
         void saveSizeAndPosition(const QString &key);
1061
         void saveSizeAndPosition(const QString &key);
1043
         void restoreSizeAndPosition(const QString &key);
1062
         void restoreSizeAndPosition(const QString &key);
1044
         void displayStatus(const QString &message = QString());
1063
         void displayStatus(const QString &message = QString());
1045
-        void setClosePrompt(QString prompt);@/
1064
+        void setClosePrompt(QString prompt);
1065
+        void setupFinished();@/
1066
+    signals:@/
1067
+        void aboutToClose(void);
1068
+        void windowReady(void);@/
1046
     protected:@/
1069
     protected:@/
1047
         void closeEvent(QCloseEvent *event);
1070
         void closeEvent(QCloseEvent *event);
1048
         void showEvent(QShowEvent *event);@/
1071
         void showEvent(QShowEvent *event);@/
1049
-    signals:@/
1050
-        void aboutToClose(void);@/
1051
     private:@/
1072
     private:@/
1052
         QString cprompt;@t\2@>@/
1073
         QString cprompt;@t\2@>@/
1053
 }@t\kern-3pt@>;
1074
 }@t\kern-3pt@>;
1108
     QMainWindow::show();
1129
     QMainWindow::show();
1109
 }
1130
 }
1110
 
1131
 
1132
+void ScriptQMainWindow::setupFinished()
1133
+{
1134
+	emit windowReady();
1135
+}
1136
+
1111
 @ When a close event occurs, we check the |windowModified| property to
1137
 @ When a close event occurs, we check the |windowModified| property to
1112
 determine if closing the window could result in loss of data. If this is
1138
 determine if closing the window could result in loss of data. If this is
1113
 true, we allow the event to be cancelled. Otherwise, a signal is emitted which
1139
 true, we allow the event to be cancelled. Otherwise, a signal is emitted which
4367
 the configuration file and load that instead of prompting for the information
4393
 the configuration file and load that instead of prompting for the information
4368
 if possible.
4394
 if possible.
4369
 
4395
 
4396
+Starting with version 1.8, if there is not a -c argument, Typica will first
4397
+search a small number of locations relative to the executable.
4398
+
4370
 @<Load the application configuration@>=
4399
 @<Load the application configuration@>=
4371
 QStringList arguments = QCoreApplication::arguments();
4400
 QStringList arguments = QCoreApplication::arguments();
4372
 int position = arguments.indexOf("-c");
4401
 int position = arguments.indexOf("-c");
4377
     {
4406
     {
4378
         filename = arguments.at(position + 1);
4407
         filename = arguments.at(position + 1);
4379
     }
4408
     }
4409
+} else {
4410
+	QDir checkPath(QCoreApplication::applicationDirPath() + "/../config/");
4411
+	if(checkPath.exists("config.xml")) {
4412
+		filename = checkPath.filePath("config.xml");
4413
+	} else {
4414
+		checkPath = QDir(QCoreApplication::applicationDirPath() + "/config/");
4415
+		if(checkPath.exists("config.xml")) {
4416
+			filename  = checkPath.filePath("config.xml");
4417
+		}
4418
+	}
4380
 }
4419
 }
4381
 if(filename.isEmpty())
4420
 if(filename.isEmpty())
4382
 {
4421
 {
4401
     {
4440
     {
4402
         app.configuration()->setContent(&file, true);
4441
         app.configuration()->setContent(&file, true);
4403
     }
4442
     }
4443
+} else {
4444
+	return 1;
4404
 }
4445
 }
4405
 @<Substitute included fragments@>@;
4446
 @<Substitute included fragments@>@;
4406
 
4447
 
4571
                          QStack<QLayout *> *layoutStack);
4612
                          QStack<QLayout *> *layoutStack);
4572
 void addSpinBoxToLayout(QDomElement element, QStack<QWidget *> *widgetStack,
4613
 void addSpinBoxToLayout(QDomElement element, QStack<QWidget *> *widgetStack,
4573
                         QStack<QLayout *> *layoutStack);
4614
                         QStack<QLayout *> *layoutStack);
4615
+void addTimeEditToLayout(QDomElement element, QStack<QWidget *> *widgetStack,
4616
+                         QStack<QLayout *> *layoutStack);
4574
 
4617
 
4575
 @ The functions for creating windows must be made available to the scripting
4618
 @ The functions for creating windows must be made available to the scripting
4576
 engine.
4619
 engine.
4679
 }
4722
 }
4680
 @<Insert help menu@>@;
4723
 @<Insert help menu@>@;
4681
 window->show();
4724
 window->show();
4725
+window->setupFinished();
4682
 
4726
 
4683
 @ Three element types make sense as top level children of a {\tt <window>}
4727
 @ Three element types make sense as top level children of a {\tt <window>}
4684
 element. An element representing a layout element can be used to apply that
4728
 element. An element representing a layout element can be used to apply that
4802
         {
4846
         {
4803
             menu->addSeparator();
4847
             menu->addSeparator();
4804
         }
4848
         }
4849
+        else if(itemElement.tagName() == "plugins")
4850
+        {
4851
+	        @<Process plugin item@>@;
4852
+        }
4805
     }
4853
     }
4806
     j++;
4854
     j++;
4807
 }
4855
 }
4915
     }
4963
     }
4916
 }
4964
 }
4917
 
4965
 
4966
+@ A common use of stacked layouts is in the creation of tabbed interfaces, but
4967
+there are also many uses in \pn{} where the tabs are not required. Therefore,
4968
+tab bar creation requires a separate XML element.
4969
+
4970
+@<Additional box layout elements@>=
4971
+else if(currentElement.tagName() == "tabbar")
4972
+{
4973
+	addTabBarToLayout(currentElement, widgetStack, layoutStack);
4974
+}
4975
+
4976
+@ The function used to create this follows the usual pattern.
4977
+
4978
+@<Functions for scripting@>=
4979
+void addTabBarToLayout(QDomElement element, QStack<QWidget*> *, QStack<QLayout*> *layoutStack)
4980
+{
4981
+	QBoxLayout *layout = qobject_cast<QBoxLayout *>(layoutStack->top());
4982
+	QTabBar *widget = new QTabBar;
4983
+	layout->addWidget(widget);
4984
+	if(!element.attribute("id").isEmpty())
4985
+	{
4986
+		widget->setObjectName(element.attribute("id"));
4987
+	}
4988
+}
4989
+
4990
+@ Rather than define the tab set in XML, this is left to the host environment.
4991
+This means that some additional scripting support is required.
4992
+
4993
+@<Set up the scripting engine@>=
4994
+constructor = engine->newFunction(constructQTabBar);
4995
+value = engine->newQMetaObject(&QTabBar::staticMetaObject, constructor);
4996
+engine->globalObject().setProperty("QTabBar", value);
4997
+
4998
+@ The constructor is trivial.
4999
+
5000
+@<Functions for scripting@>=
5001
+QScriptValue constructQTabBar(QScriptContext *, QScriptEngine *engine)
5002
+{
5003
+	QScriptValue object = engine->newQObject(new QTabBar);
5004
+	setQTabBarProperties(object, engine);
5005
+	return object;
5006
+}
5007
+
5008
+@ There are many functions that I might want to some day add support for, but
5009
+the immediate need is just creating the tabs in the first place.
5010
+
5011
+@<Functions for scripting@>=
5012
+void setQTabBarProperties(QScriptValue value, QScriptEngine *engine)
5013
+{
5014
+	setQWidgetProperties(value, engine);
5015
+	value.setProperty("addTab", engine->newFunction(QTabBar_addTab));
5016
+}
5017
+
5018
+QScriptValue QTabBar_addTab(QScriptContext *context, QScriptEngine *)
5019
+{
5020
+	QTabBar *self = getself<QTabBar *>(context);
5021
+	if(context->argumentCount() > 0)
5022
+	{
5023
+		self->addTab(argument<QString>(0, context));
5024
+	}
5025
+	else
5026
+	{
5027
+		context->throwError("Incorrect number of arguments passed to "@|
5028
+		                    "QTabBar::addTab().");
5029
+	}
5030
+	return QScriptValue();
5031
+}
5032
+
5033
+@ Function prototypes are needed.
5034
+
5035
+@<Function prototypes for scripting@>=
5036
+QScriptValue constructQTabBar(QScriptContext *context, QScriptEngine *engine);
5037
+void setQTabBarProperties(QScriptValue value, QScriptEngine *engine);
5038
+QScriptValue QTabBar_addTab(QScriptContext *context, QScriptEngine *engine);
5039
+
4918
 @ Using a grid layout is a bit different from using a box layout. Child elements
5040
 @ Using a grid layout is a bit different from using a box layout. Child elements
4919
 with various attributes are required to take full advantage of this layout type.
5041
 with various attributes are required to take full advantage of this layout type.
4920
 All direct children of a grid layout element should be {\tt <row>} elements
5042
 All direct children of a grid layout element should be {\tt <row>} elements
5050
             {
5172
             {
5051
                 addCalendarToLayout(currentElement, widgetStack, layoutStack);
5173
                 addCalendarToLayout(currentElement, widgetStack, layoutStack);
5052
             }
5174
             }
5175
+            else if(currentElement.tagName() == "timeedit")
5176
+            {
5177
+	            addTimeEditToLayout(currentElement, widgetStack, layoutStack);
5178
+            }
5053
             else if(currentElement.tagName() == "decoration")
5179
             else if(currentElement.tagName() == "decoration")
5054
             {
5180
             {
5055
                 addDecorationToLayout(currentElement, widgetStack,
5181
                 addDecorationToLayout(currentElement, widgetStack,
6203
 QScriptValue QDateTimeEdit_year(QScriptContext *context, QScriptEngine *engine);
6329
 QScriptValue QDateTimeEdit_year(QScriptContext *context, QScriptEngine *engine);
6204
 QScriptValue QDateTimeEdit_setToCurrentTime(QScriptContext *context, QScriptEngine *engine);
6330
 QScriptValue QDateTimeEdit_setToCurrentTime(QScriptContext *context, QScriptEngine *engine);
6205
 
6331
 
6332
+@ Sometimes it can be useful to allow editing a time or duration value without
6333
+a date field. For this, a |QTimeEdit| can be used.
6334
+
6335
+@<Functions for scripting@>=
6336
+void addTimeEditToLayout(QDomElement element, QStack<QWidget *> *,@|
6337
+                         QStack<QLayout *> *layoutStack)
6338
+{
6339
+	QTimeEdit *edit = new QTimeEdit;
6340
+	if(element.hasAttribute("displayFormat"))
6341
+	{
6342
+		edit->setDisplayFormat(element.attribute("displayFormat"));
6343
+	}
6344
+	else
6345
+	{
6346
+		edit->setDisplayFormat("mm:ss.zzz");
6347
+	}
6348
+	if(element.hasAttribute("id"))
6349
+	{
6350
+		edit->setObjectName(element.attribute("id"));
6351
+	}
6352
+	QBoxLayout *layout = qobject_cast<QBoxLayout *>(layoutStack->top());
6353
+	layout->addWidget(edit);
6354
+}
6355
+
6356
+@ Additional properties are added as a |QTimeEdit| is a |QDateTimeEdit|.
6357
+
6358
+@<Functions for scripting@>=
6359
+void setQTimeEditProperties(QScriptValue value, QScriptEngine *engine)
6360
+{
6361
+	setQDateTimeEditProperties(value, engine);
6362
+}
6363
+
6364
+@ A function prototype is needed.
6365
+
6366
+@<Function prototypes for scripting@>=
6367
+void setQTimeEditProperties(QScriptValue value, QScriptEngine *engine);
6368
+
6206
 @ In order to get to objects created from the XML description, it is necessary
6369
 @ In order to get to objects created from the XML description, it is necessary
6207
 to provide a function that can be called to retrieve children of a given widget.
6370
 to provide a function that can be called to retrieve children of a given widget.
6208
 When providing such an object to the script, it is necessary to determine the
6371
 When providing such an object to the script, it is necessary to determine the
6342
 {
6505
 {
6343
     setQSvgWidgetProperties(value, engine);
6506
     setQSvgWidgetProperties(value, engine);
6344
 }
6507
 }
6508
+else if(className == "QTabBar")
6509
+{
6510
+	setQTabBarProperties(value, engine);
6511
+}
6512
+else if(className == "PrinterSelector")
6513
+{
6514
+	setQComboBoxProperties(value, engine);
6515
+}
6345
 
6516
 
6346
 @ In the list of classes, the SaltTable entry is for a class which does not
6517
 @ In the list of classes, the SaltTable entry is for a class which does not
6347
 strictly exist on its own. It is, however, useful to provide some custom
6518
 strictly exist on its own. It is, however, useful to provide some custom
9004
 Starting in version 1.4, column sizes are persisted automatically using the
9175
 Starting in version 1.4, column sizes are persisted automatically using the
9005
 same method as described in the section on |SqlQueryView|.
9176
 same method as described in the section on |SqlQueryView|.
9006
 
9177
 
9178
+Starting in version 1.8, |rowCount()| is |Q_INVOKABLE|. This allows the manual
9179
+log entry interface to easily determine if any roasting data exists to save.
9180
+
9007
 @<Class declarations@>=
9181
 @<Class declarations@>=
9008
 class MeasurementModel;@/
9182
 class MeasurementModel;@/
9009
 class ZoomLog : public QTableView@/
9183
 class ZoomLog : public QTableView@/
9018
     public:@/
9192
     public:@/
9019
         ZoomLog();
9193
         ZoomLog();
9020
         QVariant data(int row, int column) const;
9194
         QVariant data(int row, int column) const;
9021
-        int rowCount();
9195
+        @[Q_INVOKABLE@,@, int rowCount();
9022
         bool saveXML(QIODevice *device);
9196
         bool saveXML(QIODevice *device);
9023
         bool saveCSV(QIODevice *device);
9197
         bool saveCSV(QIODevice *device);
9024
         QString lastTime(int series);
9198
         QString lastTime(int series);
9150
 measurements so that the |ZoomLog| thinks it gets at least one measurement
9324
 measurements so that the |ZoomLog| thinks it gets at least one measurement
9151
 every second.
9325
 every second.
9152
 
9326
 
9153
-The current approach simply replicates the last measurement every second until
9154
-the time for the most recent measurement is reached, however it would likely be
9155
-better to interpolate values between the two most recent real measurements as
9156
-this would match the graphic representation rather than altering it when later
9157
-reviewing the batch.
9327
+Prior to version 1.8 this simply replicated the last measurement every second
9328
+until the time for the most recent measurement was reached, however this yields
9329
+problematic results when loading saved data or attempting to use this view for
9330
+manual data entry. The current behavior performs a linear interpolation which
9331
+will match the graph.
9158
 
9332
 
9159
 @<Synthesize measurements for slow hardware@>=
9333
 @<Synthesize measurements for slow hardware@>=
9160
 if(lastMeasurement.contains(tempcolumn))
9334
 if(lastMeasurement.contains(tempcolumn))
9162
     if(lastMeasurement[tempcolumn].time() < measure.time())
9336
     if(lastMeasurement[tempcolumn].time() < measure.time())
9163
     {
9337
     {
9164
         QList<QTime> timelist;
9338
         QList<QTime> timelist;
9339
+        QList<double> templist;
9340
+        QTime z = QTime(0, 0, 0, 0);
9341
+        double ptime = (double)(z.secsTo(lastMeasurement[tempcolumn].time()));
9342
+        double ptemp = lastMeasurement[tempcolumn].temperature();
9343
+        double ctime = (double)(z.secsTo(measure.time()));
9344
+        double ctemp = measure.temperature();
9165
         for(QTime i = lastMeasurement.value(tempcolumn).time().addSecs(1); i < measure.time(); i = i.addSecs(1))
9345
         for(QTime i = lastMeasurement.value(tempcolumn).time().addSecs(1); i < measure.time(); i = i.addSecs(1))
9166
         {
9346
         {
9167
             timelist.append(i);
9347
             timelist.append(i);
9348
+            double v = ((ptemp * (ctime - z.secsTo(i))) + (ctemp * (z.secsTo(i) - ptime))) / (ctime - ptime);
9349
+            templist.append(v);
9168
         }
9350
         }
9169
         for(int i = 0; i < timelist.size(); i++)
9351
         for(int i = 0; i < timelist.size(); i++)
9170
         {
9352
         {
9171
             Measurement synthesized = measure;
9353
             Measurement synthesized = measure;
9172
             synthesized.setTime(timelist[i]);
9354
             synthesized.setTime(timelist[i]);
9355
+            synthesized.setTemperature(templist[i]);
9173
             newMeasurement(synthesized, tempcolumn);
9356
             newMeasurement(synthesized, tempcolumn);
9174
         }
9357
         }
9175
     }
9358
     }
12617
 
12800
 
12618
 @i webview.w
12801
 @i webview.w
12619
 
12802
 
12803
+@i printerselector.w
12804
+
12620
 @* The Application class.
12805
 @* The Application class.
12621
 
12806
 
12622
 The |Application| class represents the \pn{} program. It is responsible for
12807
 The |Application| class represents the \pn{} program. It is responsible for
14346
     value.setProperty("print", engine->newFunction(QTextEdit_print));
14531
     value.setProperty("print", engine->newFunction(QTextEdit_print));
14347
 }
14532
 }
14348
 
14533
 
14534
+@i plugins.w
14535
+
14349
 @i daterangeselector.w
14536
 @i daterangeselector.w
14350
 
14537
 
14351
 @** An area for repeated user interface elements.
14538
 @** An area for repeated user interface elements.
15378
         QModelIndex index(int row, int column,
15565
         QModelIndex index(int row, int column,
15379
                           const QModelIndex &parent = QModelIndex()) const;
15566
                           const QModelIndex &parent = QModelIndex()) const;
15380
         QModelIndex parent(const QModelIndex &child) const;
15567
         QModelIndex parent(const QModelIndex &child) const;
15381
-        int rowCount(const QModelIndex &parent = QModelIndex()) const;
15568
+        Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const;
15382
         int columnCount(const QModelIndex &parent = QModelIndex()) const;
15569
         int columnCount(const QModelIndex &parent = QModelIndex()) const;
15383
         bool setData(const QModelIndex &index, const QVariant &value,
15570
         bool setData(const QModelIndex &index, const QVariant &value,
15384
                      int role);
15571
                      int role);

+ 6
- 6
src/units.cpp View File

1
-/*279:*/
1
+/*291:*/
2
 #line 42 "./units.w"
2
 #line 42 "./units.w"
3
 
3
 
4
 #include "units.h"
4
 #include "units.h"
5
 #include <QtDebug> 
5
 #include <QtDebug> 
6
 
6
 
7
-/*:279*//*280:*/
7
+/*:291*//*292:*/
8
 #line 53 "./units.w"
8
 #line 53 "./units.w"
9
 
9
 
10
 bool Units::isTemperatureUnit(Unit unit)
10
 bool Units::isTemperatureUnit(Unit unit)
15
 unit==Rankine);
15
 unit==Rankine);
16
 }
16
 }
17
 
17
 
18
-/*:280*//*281:*/
18
+/*:292*//*293:*/
19
 #line 71 "./units.w"
19
 #line 71 "./units.w"
20
 
20
 
21
 double Units::convertTemperature(double value,Unit fromUnit,Unit toUnit)
21
 double Units::convertTemperature(double value,Unit fromUnit,Unit toUnit)
113
 return 0;
113
 return 0;
114
 }
114
 }
115
 
115
 
116
-/*:281*//*282:*/
116
+/*:293*//*294:*/
117
 #line 169 "./units.w"
117
 #line 169 "./units.w"
118
 
118
 
119
 double Units::convertRelativeTemperature(double value,Unit fromUnit,Unit toUnit)
119
 double Units::convertRelativeTemperature(double value,Unit fromUnit,Unit toUnit)
211
 return 0;
211
 return 0;
212
 }
212
 }
213
 
213
 
214
-/*:282*//*283:*/
214
+/*:294*//*295:*/
215
 #line 267 "./units.w"
215
 #line 267 "./units.w"
216
 
216
 
217
 double Units::convertWeight(double value,Unit fromUnit,Unit toUnit)
217
 double Units::convertWeight(double value,Unit fromUnit,Unit toUnit)
316
 unit==Gram);
316
 unit==Gram);
317
 }
317
 }
318
 
318
 
319
-/*:283*/
319
+/*:295*/

+ 2
- 2
src/units.h View File

1
-/*278:*/
1
+/*290:*/
2
 #line 8 "./units.w"
2
 #line 8 "./units.w"
3
 
3
 
4
 #include <QObject> 
4
 #include <QObject> 
32
 
32
 
33
 #endif
33
 #endif
34
 
34
 
35
-/*:278*/
35
+/*:290*/

+ 9
- 9
src/webelement.cpp View File

1
-/*573:*/
2
-#line 368 "./webview.w"
1
+/*586:*/
2
+#line 383 "./webview.w"
3
 
3
 
4
 #include "webelement.h"
4
 #include "webelement.h"
5
 
5
 
6
-/*571:*/
7
-#line 311 "./webview.w"
6
+/*584:*/
7
+#line 326 "./webview.w"
8
 
8
 
9
 TypicaWebElement::TypicaWebElement(QWebElement element):e(element)
9
 TypicaWebElement::TypicaWebElement(QWebElement element):e(element)
10
 {
10
 {
11
 
11
 
12
 }
12
 }
13
 
13
 
14
-/*:571*//*572:*/
15
-#line 320 "./webview.w"
14
+/*:584*//*585:*/
15
+#line 335 "./webview.w"
16
 
16
 
17
 void TypicaWebElement::appendInside(const QString&markup)
17
 void TypicaWebElement::appendInside(const QString&markup)
18
 {
18
 {
59
 e.setPlainText(text);
59
 e.setPlainText(text);
60
 }
60
 }
61
 
61
 
62
-/*:572*/
63
-#line 371 "./webview.w"
62
+/*:585*/
63
+#line 386 "./webview.w"
64
 
64
 
65
 
65
 
66
-/*:573*/
66
+/*:586*/

+ 3
- 3
src/webelement.h View File

1
-/*566:*/
2
-#line 248 "./webview.w"
1
+/*579:*/
2
+#line 263 "./webview.w"
3
 
3
 
4
 #include <QWebElement> 
4
 #include <QWebElement> 
5
 #include <QObject> 
5
 #include <QObject> 
27
 
27
 
28
 #endif
28
 #endif
29
 
29
 
30
-/*:566*/
30
+/*:579*/

+ 32
- 22
src/webview.cpp View File

1
-/*551:*/
1
+/*563:*/
2
 #line 50 "./webview.w"
2
 #line 50 "./webview.w"
3
 
3
 
4
 #include "webview.h"
4
 #include "webview.h"
5
 
5
 
6
-/*552:*/
6
+/*564:*/
7
 #line 57 "./webview.w"
7
 #line 57 "./webview.w"
8
 
8
 
9
 TypicaWebView::TypicaWebView():QWebView()
9
 TypicaWebView::TypicaWebView():QWebView()
12
 connect(page(),SIGNAL(linkClicked(QUrl)),this,SLOT(linkDelegate(QUrl)));
12
 connect(page(),SIGNAL(linkClicked(QUrl)),this,SLOT(linkDelegate(QUrl)));
13
 }
13
 }
14
 
14
 
15
-/*:552*//*553:*/
15
+/*:564*//*565:*/
16
 #line 73 "./webview.w"
16
 #line 73 "./webview.w"
17
 
17
 
18
 void TypicaWebView::linkDelegate(const QUrl&url)
18
 void TypicaWebView::linkDelegate(const QUrl&url)
20
 if(url.scheme()=="typica")
20
 if(url.scheme()=="typica")
21
 {
21
 {
22
 QString address(url.toEncoded());
22
 QString address(url.toEncoded());
23
-/*554:*/
23
+/*566:*/
24
 #line 91 "./webview.w"
24
 #line 91 "./webview.w"
25
 
25
 
26
 if(address=="typica://aboutqt")
26
 if(address=="typica://aboutqt")
29
 return;
29
 return;
30
 }
30
 }
31
 
31
 
32
-/*:554*/
32
+/*:566*/
33
 #line 79 "./webview.w"
33
 #line 79 "./webview.w"
34
 
34
 
35
-/*555:*/
35
+/*567:*/
36
 #line 100 "./webview.w"
36
 #line 100 "./webview.w"
37
 
37
 
38
 if(address.startsWith("typica://script/"))
38
 if(address.startsWith("typica://script/"))
41
 return;
41
 return;
42
 }
42
 }
43
 
43
 
44
-/*:555*/
44
+/*:567*/
45
 #line 80 "./webview.w"
45
 #line 80 "./webview.w"
46
 
46
 
47
 }
47
 }
51
 }
51
 }
52
 }
52
 }
53
 
53
 
54
-/*:553*//*556:*/
54
+/*:565*//*568:*/
55
 #line 112 "./webview.w"
55
 #line 112 "./webview.w"
56
 
56
 
57
 void TypicaWebView::load(const QString&url)
57
 void TypicaWebView::load(const QString&url)
59
 QWebView::load(QUrl(url));
59
 QWebView::load(QUrl(url));
60
 }
60
 }
61
 
61
 
62
-void TypicaWebView::print()
63
-{
64
-QPrinter*printer= new QPrinter(QPrinter::HighResolution);
65
-QPrintDialog printDialog(printer,NULL);
66
-if(printDialog.exec()==QDialog::Accepted)
67
-{
68
-QWebView::print(printer);
69
-}
70
-}
71
-
72
 void TypicaWebView::setHtml(const QString&html,const QUrl&baseUrl)
62
 void TypicaWebView::setHtml(const QString&html,const QUrl&baseUrl)
73
 {
63
 {
74
 QWebView::setHtml(html,baseUrl);
64
 QWebView::setHtml(html,baseUrl);
88
 return page()->currentFrame()->documentElement().toOuterXml();
78
 return page()->currentFrame()->documentElement().toOuterXml();
89
 }
79
 }
90
 
80
 
91
-/*:556*//*562:*/
92
-#line 205 "./webview.w"
81
+/*:568*//*569:*/
82
+#line 144 "./webview.w"
83
+
84
+void TypicaWebView::print(const QString&printerName)
85
+{
86
+QPrinter*printer= new QPrinter(QPrinter::HighResolution);
87
+if(!printerName.isEmpty())
88
+{
89
+printer->setPrinterName(printerName);
90
+printer->setFullPage(true);
91
+QWebView::print(printer);
92
+return;
93
+}
94
+QPrintDialog printDialog(printer,NULL);
95
+if(printDialog.exec()==QDialog::Accepted)
96
+{
97
+QWebView::print(printer);
98
+}
99
+}
100
+
101
+/*:569*//*575:*/
102
+#line 220 "./webview.w"
93
 
103
 
94
 QWebElement TypicaWebView::documentElement()
104
 QWebElement TypicaWebView::documentElement()
95
 {
105
 {
101
 return page()->mainFrame()->findFirstElement(selector);
111
 return page()->mainFrame()->findFirstElement(selector);
102
 }
112
 }
103
 
113
 
104
-/*:562*/
114
+/*:575*/
105
 #line 53 "./webview.w"
115
 #line 53 "./webview.w"
106
 
116
 
107
 
117
 
108
-/*:551*/
118
+/*:563*/

+ 3
- 3
src/webview.h View File

1
-/*550:*/
1
+/*562:*/
2
 #line 14 "./webview.w"
2
 #line 14 "./webview.w"
3
 
3
 
4
 #include <QWebView> 
4
 #include <QWebView> 
20
 public:
20
 public:
21
 TypicaWebView();
21
 TypicaWebView();
22
 Q_INVOKABLE void load(const QString&url);
22
 Q_INVOKABLE void load(const QString&url);
23
-Q_INVOKABLE void print();
23
+Q_INVOKABLE void print(const QString&printerName= QString());
24
 Q_INVOKABLE void setHtml(const QString&html,const QUrl&baseUrl= QUrl());
24
 Q_INVOKABLE void setHtml(const QString&html,const QUrl&baseUrl= QUrl());
25
 Q_INVOKABLE void setContent(QIODevice*device);
25
 Q_INVOKABLE void setContent(QIODevice*device);
26
 Q_INVOKABLE QString saveXml();
26
 Q_INVOKABLE QString saveXml();
34
 
34
 
35
 #endif
35
 #endif
36
 
36
 
37
-/*:550*/
37
+/*:562*/

+ 26
- 11
src/webview.w View File

31
 	public:@/
31
 	public:@/
32
 		TypicaWebView();
32
 		TypicaWebView();
33
 		@[Q_INVOKABLE@,@, void@]@, load(const QString &url);@t\2\2@>@/
33
 		@[Q_INVOKABLE@,@, void@]@, load(const QString &url);@t\2\2@>@/
34
-		@[Q_INVOKABLE@,@, void@]@, print();@t\2\2@>@/
34
+		@[Q_INVOKABLE@,@, void@]@, print(const QString &printerName = QString());@t\2\2@>@/
35
 		@[Q_INVOKABLE@,@, void@]@, setHtml(const QString &html, const QUrl &baseUrl = QUrl());@t\2\2@>@/
35
 		@[Q_INVOKABLE@,@, void@]@, setHtml(const QString &html, const QUrl &baseUrl = QUrl());@t\2\2@>@/
36
 		@[Q_INVOKABLE@,@, void@]@, setContent(QIODevice *device);@t\2\2@>@/
36
 		@[Q_INVOKABLE@,@, void@]@, setContent(QIODevice *device);@t\2\2@>@/
37
 		@[Q_INVOKABLE@,@, QString@]@, saveXml();@t\2\2@>@/
37
 		@[Q_INVOKABLE@,@, QString@]@, saveXml();@t\2\2@>@/
115
 	QWebView::load(QUrl(url));
115
 	QWebView::load(QUrl(url));
116
 }
116
 }
117
 
117
 
118
-void TypicaWebView::print()
119
-{
120
-	QPrinter *printer = new QPrinter(QPrinter::HighResolution);
121
-	QPrintDialog printDialog(printer, NULL);
122
-	if(printDialog.exec() == QDialog::Accepted)
123
-	{
124
-		QWebView::print(printer);
125
-	}
126
-}
127
-
128
 void TypicaWebView::setHtml(const QString &html, const QUrl &baseUrl)
118
 void TypicaWebView::setHtml(const QString &html, const QUrl &baseUrl)
129
 {
119
 {
130
 	QWebView::setHtml(html, baseUrl);
120
 	QWebView::setHtml(html, baseUrl);
144
 	return page()->currentFrame()->documentElement().toOuterXml();
134
 	return page()->currentFrame()->documentElement().toOuterXml();
145
 }
135
 }
146
 
136
 
137
+@ Print functionality has been extended to allow an optional argument. If the
138
+name of a printer is passed in, the print dialog will be bypassed. Note that
139
+when bypassing the print dialog, page margins are set to 0. This is intentional
140
+as the use case is for printing to more specialized printers where default page
141
+margins are not appropriate and CSS can be customized to ensure that all
142
+information fits in the printable area with the assumption of no margin.
143
+
144
+@<TypicaWebView implementation@>=
145
+void TypicaWebView::print(const QString &printerName)
146
+{
147
+	QPrinter *printer = new QPrinter(QPrinter::HighResolution);
148
+	if(!printerName.isEmpty())
149
+	{
150
+		printer->setPrinterName(printerName);
151
+		printer->setFullPage(true);
152
+		QWebView::print(printer);
153
+		return;
154
+	}
155
+	QPrintDialog printDialog(printer, NULL);
156
+	if(printDialog.exec() == QDialog::Accepted)
157
+	{
158
+		QWebView::print(printer);
159
+	}
160
+}
161
+
147
 @ Web views are exposed to the host environment in the usual manner.
162
 @ Web views are exposed to the host environment in the usual manner.
148
 
163
 
149
 @<Set up the scripting engine@>=
164
 @<Set up the scripting engine@>=

Loading…
Cancel
Save