Browse Source

Merge branch 'release-1.6.3'

Neal Wilson 10 years ago
parent
commit
5f84573e38
68 changed files with 47788 additions and 33331 deletions
  1. 2
    1
      .gitignore
  2. 1
    1
      README
  3. 54
    2
      config/Reports/auco.xml
  4. 24
    1
      config/Reports/dailyproductiondetail.xml
  5. 112
    0
      config/Reports/greensales.xml
  6. 116
    2
      config/Reports/itemtransactions.xml
  7. 5
    0
      config/Scripts/d3.min.js
  8. 116
    0
      config/Scripts/greenusechart.js
  9. 24
    10
      config/Windows/batchdetailsnew.xml
  10. 1
    1
      config/Windows/greeninventory.xml
  11. 2
    2
      config/Windows/greensales.xml
  12. 2
    2
      config/Windows/importprofiles.xml
  13. 1
    1
      config/Windows/invoicelist.xml
  14. 27
    5
      config/Windows/navigation.xml
  15. 18
    7
      config/Windows/newbatch.xml
  16. 1
    1
      config/Windows/newroaster.xml
  17. 10
    6
      config/Windows/newsamplebatch.xml
  18. 1
    1
      config/Windows/optime.xml
  19. 81
    6
      config/Windows/productionroaster.xml
  20. 1
    1
      config/Windows/profilehistory.xml
  21. 8
    8
      config/Windows/purchase.xml
  22. 3
    3
      config/Windows/roastmanager.xml
  23. 3
    0
      config/config.xml
  24. 5
    5
      src/abouttypica.cpp
  25. 2
    2
      src/abouttypica.h
  26. 61
    0
      src/advancedsettings.w
  27. 1
    1
      src/dataqsdk.w
  28. 37
    62
      src/daterangeselector.cpp
  29. 29
    3
      src/daterangeselector.h
  30. 3
    2
      src/daterangeselector.w
  31. 2
    2
      src/draglabel.cpp
  32. 2
    2
      src/draglabel.h
  33. 127
    0
      src/feedback.w
  34. 7
    6
      src/helpmenu.cpp
  35. 2
    2
      src/helpmenu.h
  36. 72
    1
      src/measurement.w
  37. 80
    0
      src/moc_abouttypica.cpp
  38. 210
    0
      src/moc_daterangeselector.cpp
  39. 80
    0
      src/moc_draglabel.cpp
  40. 92
    0
      src/moc_feedback.cpp
  41. 93
    0
      src/moc_helpmenu.cpp
  42. 111
    0
      src/moc_qextserialenumerator.cpp
  43. 176
    0
      src/moc_qextserialport.cpp
  44. 109
    0
      src/moc_scale.cpp
  45. 7631
    0
      src/moc_typica.cpp
  46. 96
    0
      src/moc_units.cpp
  47. 113
    0
      src/moc_webelement.cpp
  48. 130
    0
      src/moc_webview.cpp
  49. 491
    0
      src/phidgets.w
  50. 31397
    30656
      src/qrc_resources.cpp
  51. 1
    0
      src/resources.qrc
  52. 4
    4
      src/resources/Info.plist
  53. 22
    5
      src/resources/html/about.html
  54. 6
    6
      src/scale.cpp
  55. 2
    2
      src/scale.h
  56. 5
    1
      src/settings.w
  57. 4405
    2440
      src/typica.cpp
  58. 6
    6
      src/typica.rc
  59. 777
    16
      src/typica.w
  60. 6
    6
      src/units.cpp
  61. 2
    2
      src/units.h
  62. 736
    0
      src/unsupportedserial.w
  63. 9
    9
      src/webelement.cpp
  64. 3
    3
      src/webelement.h
  65. 24
    22
      src/webview.cpp
  66. 3
    2
      src/webview.h
  67. 4
    1
      src/webview.w
  68. 1
    1
      typica.desktop

+ 2
- 1
.gitignore View File

@@ -9,4 +9,5 @@ typica.c
9 9
 *.pdf
10 10
 *.scn
11 11
 *.toc
12
-Typica-build*
12
+Typica-build*
13
+*.o

+ 1
- 1
README View File

@@ -6,7 +6,7 @@ Project web site: http://www.randomfield.com/programs/typica/
6 6
 
7 7
 Typica is free software released under the MIT license as follows:
8 8
 
9
-Copyright 2007-2013 Neal Evan Wilson
9
+Copyright 2007-2015 Neal Evan Wilson
10 10
 
11 11
 Permission is hereby granteed, free of charge, to any person obtaining a copy
12 12
 of this software and associated documentation files (the "Software"), to deal

+ 54
- 2
config/Reports/auco.xml View File

@@ -37,7 +37,10 @@
37 37
 				QSettings.setValue("script/report_unit", unitBox.currentIndex);
38 38
 				refresh();
39 39
 			});
40
+			var rowData = new Array();
41
+			var rowIndex;
40 42
             function refresh() {
43
+				rowIndex = 0;
41 44
                 var buffer = new QBuffer;
42 45
                 buffer.open(3);
43 46
                 var output = new XmlWriter(buffer);
@@ -119,7 +122,15 @@
119 122
                 while(query.next())
120 123
                 {
121 124
                     output.writeStartElement("tr");
122
-                    output.writeTextElement("td", query.value(0));
125
+					output.writeAttribute("id", "r" + rowIndex);
126
+					output.writeStartElement("td");
127
+					output.writeStartElement("a");
128
+					output.writeAttribute("href", "typica://script/r" + rowIndex);
129
+					rowIndex++;
130
+					rowData.push(query.value(0));
131
+					output.writeCharacters(query.value(0));
132
+					output.writeEndElement();
133
+					output.writeEndElement();
123 134
                     output.writeTextElement("td", query.value(1));
124 135
                     output.writeTextElement("td", query.value(2));
125 136
 					output.writeTextElement("td", query.value(3));
@@ -146,7 +157,15 @@
146 157
                 while(query.next())
147 158
                 {
148 159
                     output.writeStartElement("tr");
149
-                    output.writeTextElement("td", query.value(0));
160
+					output.writeAttribute("id", "d" + rowIndex);
161
+					output.writeStartElement("td");
162
+					output.writeStartElement("a");
163
+					output.writeAttribute("href", "typica://script/d" + rowIndex);
164
+					rowIndex++;
165
+					rowData.push(query.value(0));
166
+					output.writeCharacters(query.value(0));
167
+					output.writeEndElement();
168
+					output.writeEndElement();
150 169
                     output.writeTextElement("td", query.value(1));
151 170
                     output.writeTextElement("td", query.value(2));
152 171
 					output.writeTextElement("td", query.value(3));
@@ -170,6 +189,39 @@
170 189
                 QSettings.setValue("auco_sort", sortBox.currentIndex);
171 190
                 refresh();
172 191
             });
192
+			report.scriptLinkClicked.connect(function(url) {
193
+				var element = new WebElement(report.findFirstElement("#" + url));
194
+				var regular = url[0] == 'r';
195
+				var index = url.slice(1, url.length);
196
+				var tableref;
197
+				if(regular) {
198
+					tableref = "regular_coffees";
199
+				} else {
200
+					tableref = "decaf_coffees";
201
+				}
202
+				var origin = rowData[Number(url.slice(1, url.length))];
203
+				var details = '<tr><td /><td colspan="6"><table><tr><th>Id</th><th>Name</th><th>Rate</th><th>Inventory</th><th>First Use</th><th>Last Use</th></tr>';
204
+				var query = new QSqlQuery();
205
+				query.prepare("SELECT id, name, (rate/:conversion1)::numeric(12,3), (stock/:conversion2)::numeric(12,3), (SELECT min(time)::date FROM use WHERE item = id) AS first_use, (SELECT max(time)::date FROM use WHERE item = id) AS last_use FROM coffee_history WHERE origin = :origin AND id IN (SELECT id FROM " + tableref + ") ORDER BY first_use DESC");
206
+				var conversion = 1;
207
+				if(unitBox.currentIndex == 0) {
208
+					conversion = 2.2;
209
+				}
210
+				query.bind(":conversion1", conversion);
211
+				query.bind(":conversion2", conversion);
212
+				query.bind(":origin", origin);
213
+				query.exec();
214
+				while(query.next()) {
215
+					details += "<tr>";
216
+					for(var i = 0; i < 6; i++) {
217
+						details += "<td>" + query.value(i) + "</td>";
218
+					}
219
+					details += "</tr>";
220
+				}
221
+				query = query.invalidate();
222
+				details += "</table></td></tr>";
223
+				element.appendOutside(details);
224
+			});
173 225
         ]]>
174 226
     </program>
175 227
 </window>

+ 24
- 1
config/Reports/dailyproductiondetail.xml View File

@@ -100,7 +100,12 @@
100 100
 					output.writeStartElement("tr");
101 101
 					output.writeTextElement("td", String(query.value(0)).split("T")[1]);
102 102
 					output.writeTextElement("td", query.value(2));
103
-					output.writeTextElement("td", query.value(11));
103
+					output.writeStartElement("td");
104
+					output.writeStartElement("a");
105
+					output.writeAttribute("href", "typica://script/p" + query.value(11).slice(1,-1));
106
+					output.writeCDATA(query.value(11));
107
+					output.writeEndElement();
108
+					output.writeEndElement();
104 109
 					var unroastedWeightsList = sqlToArray(query.value(5));
105 110
 					output.writeStartElement("td");
106 111
 					for(var i = 0; i < unroastedWeightsList.length; i++)
@@ -407,6 +412,24 @@
407 412
 			dateField.dateChanged.connect(function() {
408 413
 				refresh();
409 414
 			});
415
+			
416
+			view.scriptLinkClicked.connect(function(url) {
417
+				var arg = url.slice(1, url.length);
418
+				var details = createWindow("batchDetails");
419
+				var fakeTable = new Object;
420
+				fakeTable.holding = new Array(7);
421
+				fakeTable.data = function(r, c) {
422
+					return this.holding[c];
423
+				};
424
+				var query = new QSqlQuery();
425
+				query.exec("SELECT time, machine, (SELECT name FROM items WHERE id = roasted_id) AS name, unroasted_total_quantity AS green, roasted_quantity AS roasted, ((unroasted_total_quantity - roasted_quantity) / unroasted_total_quantity * 100::numeric)::numeric(12,2) AS weight_loss, duration, annotation FROM roasting_log WHERE files = '{" + arg + "}'");
426
+				query.next();
427
+				for(var i = 0; i < 8; i++) {
428
+					fakeTable.holding[i] = query.value(i);
429
+				}
430
+				query = query.invalidate();
431
+				details.loadData(fakeTable, 0);
432
+			});
410 433
 		]]>
411 434
 	</program>
412 435
 </window>

+ 112
- 0
config/Reports/greensales.xml View File

@@ -0,0 +1,112 @@
1
+<window id="greensales">
2
+	<reporttitle>Sales:->Green Coffee Sales</reporttitle>
3
+	<layout type="vertical">
4
+		<layout type="horizontal">
5
+			<daterange id="dates" initial="9" /><!-- Current Month to Date-->
6
+			<label>Weight Unit:</label>
7
+			<sqldrop id="unit" />
8
+			<stretch />
9
+		</layout>
10
+		<webview id="report" />
11
+	</layout>
12
+	<menu name="File">
13
+		<item id="print" shortcut="Ctrl+P">Print</item>
14
+	</menu>
15
+	<program>
16
+		<![CDATA[
17
+			this.windowTitle = "Typica - Green Coffee Sales";
18
+			var dateSelect = findChildObject(this, 'dates');
19
+			var dateQuery = new QSqlQuery();
20
+			dateQuery.exec("SELECT time::date FROM sale WHERE time = (SELECT min(time) FROM sale) OR time = (SELECT max(time) FROM sale) ORDER BY time ASC");
21
+			dateQuery.next();
22
+			var lifetimeStartDate = dateQuery.value(0);
23
+			var lifetimeEndDate;
24
+			if(dateQuery.next()) {
25
+				lifetimeEndDate = dateQuery.value(0);
26
+			} else {
27
+				lifetimeEndDate = lifetimeStartDate;
28
+			}
29
+			dateSelect.setLifetimeRange(lifetimeStartDate, lifetimeEndDate);
30
+			dateQuery = dateQuery.invalidate();
31
+			var unitBox = findChildObject(this, 'unit');
32
+			unitBox.addItem("Kg");
33
+			unitBox.addItem("Lb");
34
+			unitBox.currentIndex = QSettings.value("script/report_unit", 1);
35
+			unitBox['currentIndexChanged(int)'].connect(function() {
36
+				QSettings.setValue("script/report_unit", unitBox.currentIndex);
37
+				refresh();
38
+			});
39
+			var view = findChildObject(this, 'report');
40
+			var printMenu = findChildObject(this, 'print');
41
+			printMenu.triggered.connect(function() {
42
+				view.print();
43
+			});
44
+			var reportitems = new Array();
45
+			function refresh() {
46
+				var buffer = new QBuffer;
47
+				buffer.open(3);
48
+				var output = new XmlWriter(buffer);
49
+				var output = new XmlWriter(buffer);
50
+				output.writeStartDocument("1.0");
51
+				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">');
52
+				output.writeStartElement("html");
53
+				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
54
+				output.writeStartElement("head");
55
+				output.writeTextElement("title", "Green Coffee Sales");
56
+				output.writeEndElement();
57
+				output.writeStartElement("body");
58
+				var dateRange = dateSelect.currentRange();
59
+				var startDate = dateRange[0];
60
+				var endDate = dateRange[dateRange.length - 1];
61
+				output.writeTextElement("h1", "Green Coffee Sales: " + startDate + " - " + endDate);
62
+				var conversion = 1;
63
+				var unitText = 'Lb';
64
+				if(unitBox.currentIndex == 0) {
65
+					conversion = 2.2;
66
+					unitText = 'Kg';
67
+				}
68
+				var query = new QSqlQuery();
69
+				query.prepare("SELECT item, (SELECT name FROM coffees WHERE id = item) AS name, (SELECT origin FROM coffees WHERE id = item) AS origin, (SELECT reference FROM coffees WHERE id = item) AS reference, (SUM(quantity)/:conversion)::numeric(12,3) FROM sale WHERE time < :ed ::date + interval '1 day' AND time >= :sd GROUP BY item ORDER BY name ASC");
70
+				query.bind(":conversion", conversion);
71
+				query.bind(":ed", endDate);
72
+				query.bind(":sd", startDate);
73
+				query.exec();
74
+				output.writeStartElement("table");
75
+				output.writeAttribute("rules", "groups");
76
+				output.writeAttribute("cellpadding", "3px");
77
+				output.writeStartElement("thead");
78
+				output.writeStartElement("tr");
79
+				output.writeTextElement("th", "ID"); // 0
80
+				output.writeTextElement("th", "Coffee"); // 1
81
+				output.writeTextElement("th", "Origin"); // 2
82
+				output.writeTextElement("th", "Reference"); // 3
83
+				output.writeTextElement("th", "Quantity"); // 4
84
+				output.writeEndElement();
85
+				output.writeEndElement();
86
+				output.writeStartElement("tbody");
87
+				while(query.next()) {
88
+					output.writeStartElement("tr");
89
+					output.writeAttribute("id", "r"+query.value(0));
90
+					reportitems.push(query.value(0));
91
+					output.writeTextElement("td", query.value(0));
92
+					output.writeTextElement("td", query.value(1));
93
+					output.writeTextElement("td", query.value(2));
94
+					output.writeTextElement("td", query.value(3));
95
+					output.writeTextElement("td", query.value(4));
96
+					output.writeEndElement();
97
+				}
98
+				output.writeEndElement();
99
+				output.writeEndElement();
100
+				output.writeEndElement();
101
+				output.writeEndElement();
102
+				output.writeEndDocument();
103
+				view.setContent(buffer);
104
+				buffer.close();
105
+				query = query.invalidate();
106
+			}
107
+			refresh();
108
+			dateSelect.rangeUpdated.connect(refresh);
109
+		]]>
110
+	</program>
111
+</window>
112
+

+ 116
- 2
config/Reports/itemtransactions.xml View File

@@ -46,6 +46,9 @@
46 46
 				output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
47 47
 				output.writeStartElement("head");
48 48
 				output.writeTextElement("title", "Item Transactions");
49
+				output.writeStartElement("script");
50
+				output.writeAttribute("src", "Scripts/d3.min.js");
51
+				output.writeEndElement();
49 52
 				output.writeStartElement("style");
50 53
 				output.writeAttribute("type", "text/css");
51 54
 				output.writeCDATA("tr.PURCHASE {background-color: #77FF77}");
@@ -126,7 +129,48 @@
126 129
 					}
127 130
 					output.writeEndElement() // table
128 131
 					
129
-					query.prepare("SELECT time::date, type, quantity / :c1, balance / :c2 FROM item_history(:item)");
132
+					output.writeStartElement("div");
133
+					output.writeAttribute("id", "chart");
134
+					output.writeEndElement();
135
+					
136
+					query.prepare("WITH q AS (SELECT roasted_id, unroasted_id, unroasted_quantity, unroasted_total_quantity, roasted_quantity, generate_subscripts(unroasted_quantity, 1) AS s FROM roasting_log) SELECT (SELECT name FROM items WHERE id = roasted_id) AS name, roasted_id, SUM(unroasted_quantity[s]) AS total, COUNT(unroasted_quantity[s]), SUM((unroasted_quantity[s]/unroasted_total_quantity)*roasted_quantity)::numeric(12,3) AS roast_proportion FROM q WHERE unroasted_id[s] = :item1 GROUP BY roasted_id UNION SELECT 'Green Sales', NULL, SUM(quantity), COUNT(1), NULL FROM sale WHERE item = :item2 UNION SELECT 'Inventory Adjustment', NULL, ((SELECT SUM(quantity) FROM purchase WHERE item = :item3) - (SELECT quantity FROM items WHERE id = :item4) - (SELECT SUM(quantity) FROM all_transactions WHERE type != 'PURCHASE' AND type != 'INVENTORY' AND item = :item5)), (SELECT COUNT(1) FROM inventory WHERE item = :item6), NULL UNION SELECT 'Loss', NULL, SUM(quantity), COUNT(1), NULL FROM loss WHERE item = :item7 UNION SELECT 'Current Inventory', NULL, (SELECT quantity FROM items WHERE id = :item8), NULL, NULL ORDER BY total DESC");
137
+					query.bind(":item1", itemBox.currentData());
138
+					query.bind(":item2", itemBox.currentData());
139
+					query.bind(":item3", itemBox.currentData());
140
+					query.bind(":item4", itemBox.currentData());
141
+					query.bind(":item5", itemBox.currentData());
142
+					query.bind(":item6", itemBox.currentData());
143
+					query.bind(":item7", itemBox.currentData());
144
+					query.bind(":item8", itemBox.currentData());
145
+					query.exec();
146
+					var chartData = "var data = [";
147
+					var roastedCoffeeLines = "";
148
+					var adjustmentLines = "";
149
+					var currentInventoryLine = "";
150
+					var conversion = 1;
151
+					if(unitBox.currentIndex == 0) {
152
+						conversion = 2.2;
153
+					}
154
+					while(query.next()) {
155
+						if(Number(query.value(1)) > 0) {
156
+							roastedCoffeeLines += "['" + query.value(0).replace(/\'/g, "\\x27") + "'," + query.value(2) / conversion + "," + query.value(3) + "," + query.value(4) / conversion + "],";
157
+						} else if (query.value(0) == "Current Inventory") {
158
+							currentInventoryLine = "['Current Inventory'," + query.value(2) / conversion + "," + query.value(3) + "," + query.value(4) / conversion + "]";
159
+						} else {
160
+							if(Number(query.value(3)) > 0) {
161
+								adjustmentLines += "['" + query.value(0) + "'," + query.value(2) / conversion + "," + query.value(3) + "," + query.value(4) / conversion + "],";
162
+							}
163
+						}
164
+					}
165
+					chartData = chartData + roastedCoffeeLines + adjustmentLines + currentInventoryLine + "];";
166
+					
167
+					output.writeTextElement("script", chartData);
168
+					
169
+					output.writeStartElement("script");
170
+					output.writeAttribute("src", "Scripts/greenusechart.js");
171
+					output.writeEndElement();
172
+					
173
+					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))) FROM item_history(:item)");
130 174
 					switch(unitBox.currentIndex)
131 175
 					{
132 176
 						case 0:
@@ -146,6 +190,7 @@
146 190
 					output.writeTextElement("th", "Type");
147 191
 					output.writeTextElement("th", "Quantity");
148 192
 					output.writeTextElement("th", "Balance");
193
+					output.writeTextElement("th", "Record");
149 194
 					output.writeEndElement(); // tr
150 195
 					var prev_balance = "0";
151 196
 					var prev_prec = 0;
@@ -175,6 +220,23 @@
175 220
 						}
176 221
 						output.writeTextElement("td", query.value(3));
177 222
 						prev_balance = query.value(3);
223
+						if(query.value(1) == "PURCHASE") {
224
+							output.writeStartElement("td");
225
+							output.writeStartElement("a");
226
+							output.writeAttribute("href", "typica://script/i" + query.value(5));
227
+							output.writeCDATA(query.value(6) + " (" + query.value(5) + ")");
228
+							output.writeEndElement();
229
+							output.writeEndElement();
230
+						} else if(query.value(1) == "USE") {
231
+							output.writeStartElement("td");
232
+							output.writeStartElement("a");
233
+							output.writeAttribute("href", "typica://script/p" + query.value(4).slice(1,-1));
234
+							output.writeCDATA(query.value(7) + " " + query.value(4));
235
+							output.writeEndElement();
236
+							output.writeEndElement();
237
+						} else {
238
+							output.writeTextElement("td", "");
239
+						}
178 240
 						output.writeEndElement(); // tr
179 241
 					}
180 242
 					output.writeEndElement(); // table
@@ -193,7 +255,59 @@
193 255
 				buffer.close();
194 256
 				query = query.invalidate();
195 257
 			}
196
-			refresh();
258
+			if(itemBox.currentData() > 0) {
259
+				refresh();
260
+			}
261
+			
262
+			/* Open invoices */
263
+			var openInvoice = function(url) {
264
+				var arg = url.slice(1, url.length);
265
+				var info = createWindow("invoiceinfo");
266
+				info.setInvoiceID(arg);
267
+				var query = new QSqlQuery();
268
+				query.exec("SELECT time, invoice, vendor FROM invoices WHERE id = " + arg);
269
+				query.next();
270
+				var timefield = findChildObject(info, 'date');
271
+				timefield.text = query.value(0);
272
+				var vendorfield = findChildObject(info, 'vendor');
273
+				vendorfield.text = query.value(2);
274
+				var invoicefield = findChildObject(info, 'invoice');
275
+				invoicefield.text = query.value(1);
276
+				var itemtable = findChildObject(info, 'itemtable');
277
+				itemtable.setQuery("SELECT record_type, item_id, description, (SELECT reference FROM items WHERE id = item_id) AS reference, (SELECT cost FROM purchase WHERE item = item_id) AS unit_cost, (SELECT quantity FROM purchase WHERE item = item_id) AS quantity, ((SELECT quantity FROM purchase WHERE item = item_id)/(SELECT conversion FROM lb_bag_conversion WHERE item = item_id))::numeric(12,2) AS sacks, cost FROM invoice_items WHERE invoice_id = " + arg + " AND record_type = 'PURCHASE' UNION SELECT record_type, NULL, description, NULL, NULL, NULL, NULL, cost FROM invoice_items WHERE invoice_id = " + arg + " AND record_type = 'FEE' ORDER BY item_id");
278
+				query = query.invalidate();
279
+			};
280
+			
281
+			/* Open batch data */
282
+			var openProfile = function(url) {
283
+				var arg = url.slice(1, url.length);
284
+				var details = createWindow("batchDetails");
285
+				var fakeTable = new Object;
286
+				fakeTable.holding = new Array(7);
287
+				fakeTable.data = function(r, c) {
288
+					return this.holding[c];
289
+				};
290
+				var query = new QSqlQuery();
291
+				query.exec("SELECT time, machine, (SELECT name FROM items WHERE id = roasted_id) AS name, unroasted_total_quantity AS green, roasted_quantity AS roasted, ((unroasted_total_quantity - roasted_quantity) / unroasted_total_quantity * 100::numeric)::numeric(12,2) AS weight_loss, duration, annotation FROM roasting_log WHERE files = '{" + arg + "}'");
292
+				query.next();
293
+				for(var i = 0; i < 8; i++) {
294
+					fakeTable.holding[i] = query.value(i);
295
+				}
296
+				query = query.invalidate();
297
+				details.loadData(fakeTable, 0);
298
+			};
299
+			
300
+			view.scriptLinkClicked.connect(function(url) {
301
+				var linkType = url[0];
302
+				switch(linkType) {
303
+					case 'i':
304
+						openInvoice(url);
305
+						break;
306
+					case 'p':
307
+						openProfile(url);
308
+						break;
309
+				}
310
+			});
197 311
 		]]>
198 312
 	</program>
199 313
 </window>

+ 5
- 0
config/Scripts/d3.min.js
File diff suppressed because it is too large
View File


+ 116
- 0
config/Scripts/greenusechart.js View File

@@ -0,0 +1,116 @@
1
+// Data access functions
2
+var label = function(d) {return d[0];};
3
+var greenValue = function(d) {return d[1];};
4
+var transactionCount = function(d) {return d[2];};
5
+var roastValue = function(d) {return d[3];};
6
+
7
+// Chart parameters
8
+var valueLabelWidth = 0; // Temporary, will be updated when labels are generated
9
+var barHeight = 30;
10
+var barHeight2 = barHeight / 2;
11
+var barLabelWidth = 0;
12
+var barLabelPadding = 5;
13
+var gridLabelHeight = 18;
14
+var gridChartOffset = 3;
15
+var maxBarWidth = 500;
16
+
17
+// Scales
18
+var x1s = d3.scale.linear().domain([0, d3.max(data, function(d) { return greenValue(d); })*1.15]).range([0, maxBarWidth]);
19
+var x2s = d3.scale.linear().domain([0, d3.max(data, function(d) { return transactionCount(d); })*1.15]).range([0, maxBarWidth]);
20
+var ys = d3.scale.ordinal().domain(d3.range(0, data.length)).rangeBands([0, data.length * barHeight]);
21
+var ytext = function(d, i) {return ys(i) + ys.rangeBand() / 2;};
22
+var ybar = function(d, i) {return ys(i);};
23
+var ybar2 = function(d, i) {return ys(i) + (barHeight2 / 2)};
24
+var ybar3 = function(d, i) {return ys(i);};
25
+var ybar4 = function(d, i) {return ys(i) + barHeight;};
26
+
27
+// Chart
28
+var svg = d3.select("#chart").append("svg")
29
+	.attr("width", 1000)
30
+	.attr("height", gridLabelHeight + gridLabelHeight + gridChartOffset + data.length * barHeight);
31
+
32
+// Bar labels
33
+var yxe = svg.append("g")
34
+	.attr("class", "y axis left");
35
+yxe.selectAll("text").data(data).enter().append("text")
36
+	.attr("y", ytext)
37
+	.attr("stroke", "none")
38
+	.attr("fill", "black")
39
+	.attr("dy", ".35em")
40
+	.attr("text-anchor", "end")
41
+	.text(label);
42
+// Determine maximum label width
43
+yxe.selectAll("text").each(function() {
44
+	if(barLabelWidth < this.getComputedTextLength()) {
45
+		barLabelWidth = this.getComputedTextLength();
46
+	}
47
+});
48
+barLabelWidth += 10;
49
+yxe.attr("transform", "translate(" + (barLabelWidth - barLabelPadding) + "," + (gridLabelHeight + gridChartOffset) + ")");
50
+
51
+// Weight axis
52
+var x1xe = svg.append("g")
53
+	.attr("class", "x axis top")
54
+	.attr("transform", "translate(" + barLabelWidth + "," + gridLabelHeight + ")");
55
+	
56
+x1xe.selectAll("text").data(x1s.ticks(10)).enter().append("text")
57
+	.attr("x", x1s)
58
+	.attr("dy", -3)
59
+	.attr("text-anchor", "middle")
60
+	.text(String);
61
+// Top ticks extend approximately half way down the chart
62
+x1xe.selectAll("line").data(x1s.ticks(10)).enter().append("line")
63
+	.attr("x1", x1s)
64
+	.attr("x2", x1s)
65
+	.attr("y1", 0)
66
+	.attr("y2", ys.rangeExtent()[1] /2)
67
+	.style("stroke", "#ccc");
68
+
69
+// Transaction count axis
70
+var x2xe = svg.append("g")
71
+	.attr("class", "x axis bottom")
72
+	.attr("transform", "translate(" + barLabelWidth + "," + (ys.rangeExtent()[1] + gridChartOffset + gridLabelHeight + gridLabelHeight) + ")");
73
+
74
+x2xe.selectAll("text").data(x2s.ticks(10)).enter().append("text")
75
+	.attr("x", x2s)
76
+	.attr("dy", -3)
77
+	.attr("text-anchor", "middle")
78
+	.text(String);
79
+// Bottom ticks extend approximately half way up the chart	
80
+x2xe.selectAll("line").data(x2s.ticks(10)).enter().append("line")
81
+	.attr("x1", x2s)
82
+	.attr("x2", x2s)
83
+	.attr("y1", -gridLabelHeight - (ys.rangeExtent()[1] /2))
84
+	.attr("y2", -gridLabelHeight)
85
+	.style("stroke", "#ccc");
86
+
87
+// Green coffee bars
88
+var gbars = svg.append("g")
89
+	.attr("transform", "translate(" + barLabelWidth + "," + (gridLabelHeight + gridChartOffset) + ")");
90
+gbars.selectAll("rect").data(data).enter().append("rect")
91
+	.attr("y", ybar)
92
+	.attr("height", barHeight)
93
+	.attr("width", function(d) {return x1s(greenValue(d));})
94
+	.attr("stroke", "white")
95
+	.attr("fill", "royalblue");
96
+
97
+// Roasted coffee bars
98
+var rbars = svg.append("g")
99
+	.attr("transform", "translate(" + barLabelWidth + "," + (gridLabelHeight + gridChartOffset) + ")");
100
+rbars.selectAll("rect").data(data).enter().append("rect")
101
+	.attr("y", ybar2)
102
+	.attr("height", barHeight2)
103
+	.attr("width", function(d) {return x1s(roastValue(d));})
104
+	.attr("stroke", "white")
105
+	.attr("fill", "orangered");
106
+
107
+// Transaction ticks
108
+var tticks = svg.append("g")
109
+	.attr("transform", "translate(" + barLabelWidth + "," + (gridLabelHeight + gridChartOffset) + ")");
110
+tticks.selectAll("line").data(data).enter().append("line")
111
+	.attr("x1", function(d) {return x2s(transactionCount(d));})
112
+	.attr("x2", function(d) {return x2s(transactionCount(d));})
113
+	.attr("y1", ybar3)
114
+	.attr("y2", ybar4)
115
+	.attr("stroke-width", "3")
116
+	.style("stroke", "black");

+ 24
- 10
config/Windows/batchdetailsnew.xml View File

@@ -8,6 +8,9 @@
8 8
         </layout>
9 9
         <webview id="view" />
10 10
     </layout>
11
+	<menu name="File">
12
+		<item id="save" shortcut="Ctrl+S">Save…</item>
13
+	</menu>
11 14
     <program>
12 15
         <![CDATA[
13 16
 			var window = this;
@@ -17,7 +20,7 @@
17 20
 			var compare = findChildObject(this, 'compare');
18 21
 			var edit = findChildObject(this, 'edit');
19 22
 			edit.enabled = false;
20
-			if(typeof(navigationwindow.loggingWindow) == "undefined") {
23
+			if(typeof(Windows.loggingWindow) == "undefined") {
21 24
 				compare.enabled = false;
22 25
 				target.enabled = false;
23 26
 			}
@@ -44,7 +47,7 @@
44 47
                 var nextSeries = startSeries + 2;
45 48
                 QSettings.setValue('cseries', nextSeries);
46 49
                 var input = new XMLInput(buffer, startSeries);
47
-                var graph = findChildObject(navigationwindow.loggingWindow, 'graph');
50
+                var graph = findChildObject(Windows.loggingWindow, 'graph');
48 51
                 input.measure.connect(graph.newMeasurement);
49 52
                 input.input();
50 53
 				query = query.invalidate();
@@ -59,13 +62,13 @@
59 62
 				var pname = query.value(1);
60 63
                 query = query.invalidate();
61 64
 				var input = new XMLInput(buffer, 1);
62
-				var graph = findChildObject(navigationwindow.loggingWindow, 'graph');
63
-				var log = findChildObject(navigationwindow.loggingWindow, 'log');
65
+				var graph = findChildObject(Windows.loggingWindow, 'graph');
66
+				var log = findChildObject(Windows.loggingWindow, 'log');
64 67
 				log.clear();
65 68
 				graph.clear();
66 69
 				input.newTemperatureColumn.connect(log.setHeaderData);
67 70
 				input.newTemperatureColumn.connect(function(col, text) {
68
-					if(text == navigationwindow.loggingWindow.targetcolumnname)
71
+					if(text == Windows.loggingWindow.targetcolumnname)
69 72
 					{
70 73
 						targetseries = col;
71 74
 					}
@@ -83,7 +86,7 @@
83 86
 				input.lastColumn.connect(function(c) {
84 87
 					lc = c;
85 88
 					QSettings.setValue("liveColumn", c + 1);
86
-					navigationwindow.loggingWindow.postLoadColumnSetup(c);
89
+					Windows.loggingWindow.postLoadColumnSetup(c);
87 90
 				});
88 91
 				input.annotation.connect(log.newAnnotation);
89 92
 				input.annotation.connect(function(note, tcol, ncol) {
@@ -91,9 +94,9 @@
91 94
 						log.newAnnotation(note, i, ncol);
92 95
 					}
93 96
 				});
94
-				navigationwindow.loggingWindow.windowTitle = "Typica - " + pname;
95
-				navigationwindow.loggingWindow.raise();
96
-				navigationwindow.loggingWindow.activateWindow();
97
+				Windows.loggingWindow.windowTitle = "Typica - " + pname;
98
+				Windows.loggingWindow.raise();
99
+				Windows.loggingWindow.activateWindow();
97 100
 				input.input();
98 101
 				log.newAnnotation("End", 1, lc);
99 102
 				query = query.invalidate();
@@ -139,7 +142,11 @@
139 142
 					viewer.windowTitle = "Typica - " + pname;
140 143
 					viewer.raise();
141 144
 					viewer.activateWindow();
145
+					graph.updatesEnabled = false;
146
+					log.updatesEnabled = false;
142 147
 					input.input();
148
+					log.updatesEnabled = true;
149
+					graph.updatesEnabled = true;
143 150
 					log.newAnnotation("End", 1, lc);
144 151
 				}
145 152
 				else {
@@ -209,7 +216,7 @@
209 216
 					nameQuery.bind(":id", items[i]);
210 217
 					nameQuery.exec();
211 218
 					nameQuery.next();
212
-					output.writeTextElement("td", nameQuery.value(0));
219
+					output.writeTextElement("td", nameQuery.value(0) + " (" + items[i] + ")");
213 220
 					output.writeStartElement("td");
214 221
 					output.writeAttribute("align", "center");
215 222
 					output.writeCharacters(quantities[i]);
@@ -330,6 +337,13 @@
330 337
 				query = query.invalidate();
331 338
 				edit.enabled = true;
332 339
 			};
340
+			var saveMenu = findChildObject(this, 'save');
341
+			saveMenu.triggered.connect(function() {
342
+				var filename = QFileDialog.getSaveFileName(window, "Save Log As…", QSettings.value("script/lastDir", "") + "/");
343
+				if(filename != "") {
344
+					saveFileFromDatabase(fileID, filename);
345
+				}
346
+			});
333 347
         ]]>
334 348
     </program>
335 349
 </window>

+ 1
- 1
config/Windows/greeninventory.xml View File

@@ -40,7 +40,7 @@
40 40
 			var button = findChildObject(this, 'update');
41 41
 			var value = findChildObject(this, 'quantity');
42 42
 			button.clicked.connect(function() {
43
-				q = "INSERT INTO inventory VALUES ('now', ";
43
+				q = "INSERT INTO inventory (time, item, quantity) VALUES ('now', ";
44 44
 				q = q + items.currentData();
45 45
 				q = q + ", ";
46 46
 				if(units.currentText == "lb") {

+ 2
- 2
config/Windows/greensales.xml View File

@@ -2,7 +2,7 @@
2 2
 	<layout type="vertical">
3 3
 		<layout type="horizontal">
4 4
 			<label>Date:</label>
5
-			<calendar id="date" />
5
+			<calendar time="true" id="date" />
6 6
 			<label>Customer:</label>
7 7
 			<line id="customer" />
8 8
 			<label>Weight Unit:</label>
@@ -53,7 +53,7 @@
53 53
 			var submit = findChildObject(this, 'submit');
54 54
 			submit.clicked.connect(function() {
55 55
 				var query = new QSqlQuery();
56
-				query.prepare("INSERT INTO sale VALUES(:time, :item, :quantity, :customer)");
56
+				query.prepare("INSERT INTO sale (time, item, quantity, customer) VALUES(:time, :item, :quantity, :customer)");
57 57
 				query.bind(":time", dateField.text);
58 58
 				if(customerField.text == "") {
59 59
 					query.bind(":customer", null);

+ 2
- 2
config/Windows/importprofiles.xml View File

@@ -13,7 +13,7 @@
13 13
             var filename = QFileDialog.getOpenFileName(win, 'Open Log…', QSettings.value('script/lastDir', '') + '/');
14 14
             if(filename != '') {
15 15
                 QSettings.setValue("script/lastDir", dir(filename));
16
-                var q = "INSERT INTO files VALUES(default, :name, 'profile', NULL, :data) RETURNING id";
16
+                var q = "INSERT INTO files (id, name, type, note, file) VALUES(default, :name, 'profile', NULL, :data) RETURNING id";
17 17
                 query = new QSqlQuery();
18 18
                 query.prepare(q);
19 19
                 query.bind(":name", baseName(filename));
@@ -22,7 +22,7 @@
22 22
                 query.next();
23 23
                 var fileno = query.value(0);
24 24
                 var id = box.currentData();
25
-                q = "INSERT INTO item_files VALUES('now', ";
25
+                q = "INSERT INTO item_files (time, item, files) VALUES('now', ";
26 26
                 q = q + id;
27 27
                 q = q + ", '{";
28 28
                 q = q + fileno;

+ 1
- 1
config/Windows/invoicelist.xml View File

@@ -6,7 +6,7 @@
6 6
 		<![CDATA[
7 7
 			this.windowTitle = "Typica - Invoice List";
8 8
 			var table = findChildObject(this, 'table');
9
-			table.setQuery("SELECT id, time, invoice, vendor FROM invoices ORDER BY time DESC");
9
+			table.setQuery("SELECT id, time, invoice, vendor, (SELECT sum(cost) FROM invoice_items WHERE invoice_id = id) AS cost FROM invoices ORDER BY time DESC");
10 10
 			table.openEntry.connect(function(arg) {
11 11
 				var info = createWindow("invoiceinfo");
12 12
 				info.setInvoiceID(arg);

+ 27
- 5
config/Windows/navigation.xml View File

@@ -210,8 +210,6 @@ type="push" />
210 210
 			query.exec("CREATE VIEW short_log AS SELECT roasting_log.\"time\", (SELECT items.name FROM items WHERE (items.id = roasting_log.roasted_id)) AS name, roasting_log.unroasted_total_quantity, roasting_log.roasted_quantity, ((((roasting_log.unroasted_total_quantity - roasting_log.roasted_quantity) / roasting_log.unroasted_total_quantity) * (100)::numeric))::numeric(12,2) AS weight_loss, roasting_log.duration FROM roasting_log ORDER BY roasting_log.\"time\"");
211 211
 			query.exec("CREATE FUNCTION add_inventory() RETURNS trigger AS $$ BEGIN UPDATE items SET quantity = quantity + NEW.quantity WHERE id = NEW.item; RETURN NEW; END; $$ LANGUAGE plpgsql");
212 212
 			query.exec("CREATE FUNCTION bags_in_stock(bigint) RETURNS numeric AS $_$SELECT quantity / (SELECT conversion FROM lb_bag_conversion WHERE item = id) FROM items WHERE id = $1;$_$ LANGUAGE sql IMMUTABLE STRICT");
213
-			query.exec("CREATE FUNCTION log_make() RETURNS trigger AS $$ BEGIN IF NEW.roasted_quantity IS NOT NULL THEN INSERT INTO make VALUES(NEW.time, NEW.roasted_id, NEW.roasted_quantity); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql");
214
-			query.exec("CREATE FUNCTION log_make_update() RETURNS trigger AS $$ BEGIN IF NEW.roasted_quantity <> OLD.roasted_quantity AND NEW.roasted_quantity IS NOT NULL THEN INSERT INTO make VALUES(NEW.time, NEW.roasted_id, NEW.roasted_quantity); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql");
215 213
 			query.exec("CREATE FUNCTION log_use() RETURNS trigger AS $$ DECLARE i integer := array_lower(NEW.unroasted_id, 1); u integer := array_upper(NEW.unroasted_id, 1); BEGIN WHILE i <= u LOOP INSERT INTO use VALUES(NEW.time, NEW.unroasted_id[i], NEW.unroasted_quantity[i]); i := i + 1; END LOOP; RETURN NEW; END; $$ LANGUAGE plpgsql");
216 214
 			query.exec("CREATE FUNCTION replace_inventory() RETURNS trigger AS $$ BEGIN UPDATE items SET quantity = NEW.quantity WHERE id = NEW.item; RETURN NEW; END; $$ LANGUAGE plpgsql");
217 215
 			query.exec("CREATE FUNCTION subtract_inventory() RETURNS trigger AS $$ BEGIN UPDATE items SET quantity = quantity - NEW.quantity WHERE id = NEW.item; RETURN NEW; END; $$ LANGUAGE plpgsql");
@@ -236,7 +234,7 @@ type="push" />
236 234
 			query.exec("CREATE TRIGGER subtract_inventory_trigger AFTER INSERT ON use FOR EACH ROW EXECUTE PROCEDURE subtract_inventory()");
237 235
 			query.exec("ALTER TABLE ONLY item_files ADD CONSTRAINT item_files_item_fkey FOREIGN KEY (item) REFERENCES items(id)");
238 236
 			query.exec("ALTER TABLE ONLY transactions ADD CONSTRAINT transactions_item_fkey FOREIGN KEY (item) REFERENCES items(id)");
239
-			query.exec("INSERT INTO TypicaFeatures VALUES('base-features', TRUE, 1)");
237
+			query.exec("INSERT INTO TypicaFeatures (feature, enabled, version) VALUES('base-features', TRUE, 1)");
240 238
 			query = query.invalidate();
241 239
 		};
242 240
 		
@@ -244,10 +242,9 @@ type="push" />
244 242
 		   Typica 1.6 and later. */
245 243
 		var DBCreateSampleRoasting = function() {
246 244
 			var query = new QSqlQuery;
247
-			query.exec("CREATE OR REPLACE FUNCTION log_use() RETURNS trigger AS $$ DECLARE i integer := array_lower(NEW.unroasted_id, 1); u integer := array_upper(NEW.unroasted_id, 1); BEGIN IF NEW.transaction_type = 'ROAST' THEN WHILE i <= u LOOP INSERT INTO use VALUES(NEW.time, NEW.unroasted_id[i], NEW.unroasted_quantity[i]); i := i + 1; END LOOP; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql");
248 245
 			query.exec("CREATE TABLE IF NOT EXISTS item_attributes (id bigint PRIMARY KEY NOT NULL, name text NOT NULL)");
249 246
 			query.exec("CREATE TABLE IF NOT EXISTS coffee_sample_items(arrival timestamp without time zone, vendor text, attribute_ids bigint[], attribute_values text[], item_id bigint) INHERITS (items)");
250
-			query.exec("INSERT INTO TypicaFeatures VALUES('sample-roasting', TRUE, 1)");
247
+			query.exec("INSERT INTO TypicaFeatures (feature, enabled, version) VALUES('sample-roasting', TRUE, 1)");
251 248
 			query = query.invalidate();
252 249
 		};
253 250
 		
@@ -280,6 +277,23 @@ type="push" />
280 277
 			query = query.invalidate();
281 278
 		};
282 279
 		
280
+		/* Asynchronous notifications */
281
+		var DBUpdateNotifications = function() {
282
+			var query = new QSqlQuery;
283
+			query.exec("CREATE OR REPLACE FUNCTION notify_roasting_log_change() RETURNS TRIGGER AS $$ BEGIN NOTIFY RoastingLogChange; RETURN NULL; END; $$ LANGUAGE plpgsql");
284
+			query.exec("CREATE TRIGGER notify_roasting_log_change AFTER INSERT OR UPDATE ON roasting_log FOR EACH STATEMENT EXECUTE PROCEDURE notify_roasting_log_change()");
285
+			query = query.invalidate();
286
+		};
287
+		
288
+		/* Update trigger functions to make column names explicit */
289
+		var DBUpdateTriggers = function() {
290
+			var query = new QSqlQuery;
291
+			query.exec("CREATE OR REPLACE FUNCTION log_make() RETURNS trigger AS $$ BEGIN IF NEW.roasted_quantity IS NOT NULL THEN INSERT INTO make (time, item, quantity) VALUES(NEW.time, NEW.roasted_id, NEW.roasted_quantity); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql");
292
+			query.exec("CREATE OR REPLACE FUNCTION log_make_update() RETURNS trigger AS $$ BEGIN IF NEW.roasted_quantity <> OLD.roasted_quantity AND NEW.roasted_quantity IS NOT NULL THEN INSERT INTO make (time, item, quantity) VALUES(NEW.time, NEW.roasted_id, NEW.roasted_quantity); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql");
293
+			query.exec("CREATE OR REPLACE FUNCTION log_use() RETURNS trigger AS $$ DECLARE i integer := array_lower(NEW.unroasted_id, 1); u integer := array_upper(NEW.unroasted_id, 1); BEGIN IF NEW.transaction_type = 'ROAST' THEN WHILE i <= u LOOP INSERT INTO use (time, item, quantity) VALUES(NEW.time, NEW.unroasted_id[i], NEW.unroasted_quantity[i]); i := i + 1; END LOOP; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql");
294
+			query.exec("UPDATE TypicaFeatures SET version = 4 WHERE feature = 'base-features'");
295
+		};
296
+		
283 297
 		query = new QSqlQuery();
284 298
 		/* A table keeps track of database versioning information. This table is created
285 299
 		   if required. */
@@ -299,6 +313,14 @@ type="push" />
299 313
 				DBUpdateMultiUser();
300 314
 				DBUpdateHistory();
301 315
 			}
316
+			if(query.value(2) < 3)
317
+			{
318
+				DBUpdateNotifications();
319
+			}
320
+			if(query.value(2) < 4)
321
+			{
322
+				DBUpdateTriggers();
323
+			}
302 324
 		}
303 325
 		else
304 326
 		{

+ 18
- 7
config/Windows/newbatch.xml View File

@@ -244,6 +244,8 @@
244 244
                 query.bind(":item", Number(roasted.currentData()));
245 245
                 query.bind(":again", Number(roasted.currentData()));
246 246
 				query.exec();
247
+				var graph;
248
+				var log;
247 249
 				if(query.next())
248 250
 				{
249 251
 					var files = query.value(0);
@@ -259,8 +261,8 @@
259 261
 						var buffer = new QBuffer(query.value(0));
260 262
 						var pname = query.value(1);
261 263
 						var input = new XMLInput(buffer, 1);
262
-						var graph = findChildObject(navigationwindow.loggingWindow, 'graph');
263
-						var log = findChildObject(navigationwindow.loggingWindow, 'log');
264
+						graph = findChildObject(navigationwindow.loggingWindow, 'graph');
265
+						log = findChildObject(navigationwindow.loggingWindow, 'log');
264 266
 						log.clear();
265 267
 						graph.clear();
266 268
 						input.newTemperatureColumn.connect(log.setHeaderData);
@@ -279,20 +281,28 @@
279 281
 								targetDetector.newMeasurement(data);
280 282
 							}
281 283
 						});
282
-						input.annotation.connect(log.newAnnotation);
283 284
 						var lc;
284 285
 						input.lastColumn.connect(function(c) {
285 286
 							lc = c;
286 287
 							QSettings.setValue("liveColumn", c + 1);
287 288
 							navigationwindow.loggingWindow.postLoadColumnSetup(c)
288 289
 						});
290
+						input.annotation.connect(function(note, tcol, ncol) {
291
+							for(var i = tcol; i < ncol; i++) {
292
+								log.newAnnotation(note, i, ncol);
293
+							}
294
+						});
289 295
 					}
290 296
 				}
291 297
                 query = query.invalidate();
292 298
 				navigationwindow.loggingWindow.windowTitle = "Typica - [*]" + pname;
293 299
 				navigationwindow.loggingWindow.raise();
294 300
 				navigationwindow.loggingWindow.activateWindow();
301
+				graph.updatesEnabled = false;
302
+				log.updatesEnabled = false;
295 303
 				input.input();
304
+				log.updatesEnabled = true;
305
+				graph.updatesEnabled = true;
296 306
 				log.newAnnotation("End", 1, lc);
297 307
 			});
298 308
 			var noprofilebutton = findChildObject(this, 'noprofile');
@@ -307,19 +317,20 @@
307 317
 			var notes = findChildObject(this, 'annotation');
308 318
 			var duration = findChildObject(this, 'duration');
309 319
 			var approval = findChildObject(this, 'approval');
320
+			approval.checked = true;
310 321
 			var target = findChildObject(this, 'target');
311 322
 			submitbutton.clicked.connect(function() {
312 323
 				checkQuery = new QSqlQuery();
313 324
 				checkQuery.exec("SELECT 1 FROM machine WHERE id = " + selectedRoasterID);
314 325
 				if(!checkQuery.next())
315 326
 				{
316
-					checkQuery.prepare("INSERT INTO machine VALUES(:id, :name)");
327
+					checkQuery.prepare("INSERT INTO machine (id, name) VALUES(:id, :name)");
317 328
 					checkQuery.bind(":id", selectedRoasterID);
318 329
 					checkQuery.bind(":name", selectedRoasterName);
319 330
 					checkQuery.exec();
320 331
 				}
321 332
 				checkQuery = checkQuery.invalidate();
322
-				var q = "INSERT INTO files VALUES(default, :name, 'profile', NULL, :data) RETURNING id";
333
+				var q = "INSERT INTO files (id, name, type, note, file) VALUES(default, :name, 'profile', NULL, :data) RETURNING id";
323 334
 				query = new QSqlQuery();
324 335
 				query.prepare(q);
325 336
 				query.bind(":name", timefield.text + " " + roasted.currentText);
@@ -329,7 +340,7 @@
329 340
 				var fileno = query.value(0);
330 341
 				var file = new QFile(batch.tempData);
331 342
 				file.remove();
332
-				var q2 = "INSERT INTO roasting_log VALUES(:time, ";
343
+				var q2 = "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, ";
333 344
 				q2 = q2 + table.columnArray(0, 32);
334 345
 				q2 = q2 + ", ";
335 346
 				for(var i = 0; table.data(i, 1, 0).value != ""; i++)
@@ -357,7 +368,7 @@
357 368
 				query2.exec();
358 369
                 query2 = query2.invalidate();
359 370
 				if(target.checked) {
360
-					var q3 = "INSERT INTO item_files VALUES(:time, :item, '{";
371
+					var q3 = "INSERT INTO item_files (time, item, files) VALUES(:time, :item, '{";
361 372
 					q3 = q3 + fileno;
362 373
 					q3 = q3 + "}')";
363 374
 					query.prepare(q3);

+ 1
- 1
config/Windows/newroaster.xml View File

@@ -17,7 +17,7 @@
17 17
 			var rnumber = findChildObject(this, 'id');
18 18
 			var nrwindow = this;
19 19
 			button.clicked.connect(function() {
20
-				var q = "INSERT INTO machine VALUES (";
20
+				var q = "INSERT INTO machine (id, name) VALUES (";
21 21
 				q = q + rnumber.text;
22 22
 				q = q + ", :name)";
23 23
 				query = new QSqlQuery();

+ 10
- 6
config/Windows/newsamplebatch.xml View File

@@ -227,7 +227,11 @@
227 227
 						});
228 228
 						navigationwindow.loggingWindow.raise();
229 229
 						navigationwindow.loggingWindow.activateWindow();
230
+						log.updatesEnabled = false;
231
+						graph.updatesEnabled = false;
230 232
 						input.input();
233
+						graph.updatesEnabled = true;
234
+						log.updatesEnabled = true;
231 235
 						log.newAnnotation("End", 1, lc);
232 236
 					}
233 237
 				}
@@ -248,7 +252,7 @@
248 252
 			var target = findChildObject(this, 'target');
249 253
 			submit.clicked.connect(function() {
250 254
 				query = new QSqlQuery();
251
-				query.prepare("INSERT INTO files VALUES(DEFAULT, :name, 'profile', NULL, :data) RETURNING id");
255
+				query.prepare("INSERT INTO files (id, name, type, note, file) VALUES(DEFAULT, :name, 'profile', NULL, :data) RETURNING id");
252 256
 				query.bind(":name", timefield.text + " " + name.text + " " + profileName.currentText);
253 257
 				query.bindFileData(":data", batch.tempData);
254 258
 				query.exec();
@@ -258,7 +262,7 @@
258 262
 				file.remove();
259 263
 				if(target.checked)
260 264
 				{
261
-					query.prepare("INSERT INTO sample_roast_profiles VALUES(:time, :name, :file)");
265
+					query.prepare("INSERT INTO sample_roast_profiles (time, profile_name, file) VALUES(:time, :name, :file)");
262 266
 					query.bind(":time", timefield.text);
263 267
 					query.bind(":name", profileName.currentText);
264 268
 					query.bind(":file", Number(fileno));
@@ -289,14 +293,14 @@
289 293
 					}
290 294
 					else
291 295
 					{
292
-						query.prepare("INSERT INTO item_attributes VALUES(DEFAULT, :name) RETURNING id");
296
+						query.prepare("INSERT INTO item_attributes (id, name) VALUES(DEFAULT, :name) RETURNING id");
293 297
 						query.bind(":name", attname);
294 298
 						query.exec();
295 299
 						query.next();
296 300
 						attributes.setData(i, 0, query.value(0), 32);
297 301
 					}
298 302
 				}
299
-				query.prepare("INSERT INTO coffee_sample_items VALUES(DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Green Sample', :arrival, :vendor, :attrids, :attrvals, NULL) RETURNING id");
303
+				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");
300 304
 				query.bind(":name", name.text);
301 305
 				query.bind(":arrival", arrival.date);
302 306
 				query.bind(":vendor", vendor.text);
@@ -305,12 +309,12 @@
305 309
 				query.exec();
306 310
 				query.next();
307 311
 				var greenId = query.value(0);
308
-				query.prepare("INSERT INTO items VALUES(DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Roasted Sample') RETURNING id");
312
+				query.prepare("INSERT INTO items (id, name, reference, unit, quantity, category) VALUES(DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Roasted Sample') RETURNING id");
309 313
 				query.bind(":name", name.text + " " + profileName.currentText);
310 314
 				query.exec();
311 315
 				query.next();
312 316
 				var roastedId = query.value(0);
313
-				query.prepare("INSERT INTO roasting_log VALUES(:time, :unroastedids, NULL, :green, :roastedid, :roasted, 'SAMPLEROAST', :note, :machine, :duration, TRUE, NULL, NULL, NULL, NULL, :files)");
317
+				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, NULL, :green, :roastedid, :roasted, 'SAMPLEROAST', :note, :machine, :duration, TRUE, NULL, NULL, NULL, NULL, :files)");
314 318
 				query.bind(":time", timefield.text);
315 319
 				query.bind(":unroastedids", "{" + greenId + "}");
316 320
 				query.bind(":green", convertToPounds(parseFloat(green.text), GunitBox.currentText));

+ 1
- 1
config/Windows/optime.xml View File

@@ -63,7 +63,7 @@
63 63
 			});
64 64
 			submitbutton.clicked.connect(function() {
65 65
 				query = new QSqlQuery();
66
-				var q = "INSERT INTO operational_time VALUES (:machine, :start, :stop, :duration)";
66
+				var q = "INSERT INTO operational_time (machine, start_time, end_time, duration) VALUES (:machine, :start, :stop, :duration)";
67 67
 				query.prepare(q);
68 68
 				query.bind(":machine", machine.currentData());
69 69
 				query.bind(":start", startline.text);

+ 81
- 6
config/Windows/productionroaster.xml View File

@@ -64,10 +64,13 @@
64 64
 		var translationCurrentColumn = -1;
65 65
 		var configModel = new DeviceTreeModel;
66 66
 		var rootIndex = configModel.index(roasterlist.currentIndex, 0);
67
+		var roasterIndex = roasterlist.currentIndex;
67 68
 		var channels = new Array();
68 69
 		var annotationButtons = new Array();
69 70
 		var nidevices = new Array();
70 71
 		var dataqsdkdevices = new Array();
72
+		var jsdevices = new Array();
73
+		var pdevices = new Array();
71 74
 		var temperatureDisplays = new Array();
72 75
 		var columnNames = new Array();
73 76
 		var modbusdevices = new Array();
@@ -356,6 +359,57 @@
356 359
 						tabControls.push(outputControl);
357 360
 					}
358 361
 				}
362
+				else if(driverReference.driver == "unsupporteddevice")
363
+				{
364
+					var device = createDevice(driverIndex);
365
+					for(var j = 0; j < device.channelCount(); j++) {
366
+						channels.push(device.getChannel(j));
367
+						columnNames.push(device.channelColumnName(j));
368
+						var unit = device.expectedChannelUnit(j);
369
+						if(unit == Units.Fahrenheit) {
370
+							channelType.push("T");
371
+						} else {
372
+							channelType.push("C");
373
+						}
374
+						if(device.isChannelHidden(j)) {
375
+							channelVisibility.push(false);
376
+						} else {
377
+							channelVisibility.push(true);
378
+							var indicator = new TemperatureDisplay;
379
+							if(unit == Units.Unitless) {
380
+								indicator.setDisplayUnits(Units.Unitless);
381
+								indicator.digitCount = 6;
382
+							}
383
+							temperatureDisplays.push(indicator);
384
+							var decorator = new WidgetDecorator(indicator, device.channelIndicatorText(j), 2);
385
+							device.getChannel(j).newData.connect(indicator.setValue);
386
+							indicatorPanel.addWidget(decorator);
387
+						}
388
+					}
389
+					device.start();
390
+					jsdevices.push(device);
391
+				}
392
+				else if(driverReference.driver == "phidgets1048")
393
+				{
394
+					var device = new PhidgetsTemperatureSensor(driverIndex);
395
+					for(var j = 0; j < device.channelCount(); j++) {
396
+						channels.push(device.getChannel(j));
397
+						columnNames.push(device.channelColumnName(j));
398
+						channelType.push("T");
399
+						if(device.isChannelHidden(j)) {
400
+							channelVisibility.push(false);
401
+						} else {
402
+							channelVisibility.push(true);
403
+							var indicator = new TemperatureDisplay;
404
+							temperatureDisplays.push(indicator);
405
+							var decorator = new WidgetDecorator(indicator, device.channelIndicatorText(j), 2);
406
+							device.getChannel(j).newData.connect(indicator.setValue);
407
+							indicatorPanel.addWidget(decorator);
408
+						}
409
+					}
410
+					device.start();
411
+					pdevices.push(device);
412
+				}
359 413
 				else if(driverReference.driver == "annotationbutton")
360 414
 				{
361 415
 					var button = new AnnotationButton(driverReference.buttontext);
@@ -598,20 +652,36 @@
598 652
 			{
599 653
 				dataqsdkdevices[i].deleteLater();
600 654
 			}
655
+			for(var i = 0; i < jsdevices.length; i++)
656
+			{
657
+				jsdevices[i].stop();
658
+				jsdevices[i].deleteLater();
659
+			}
660
+			for(var i = 0; i < pdevices.length; i++)
661
+			{
662
+				pdevices[i].stop();
663
+				pdevices[i].deleteLater();
664
+			}
601 665
 			delete nidevices;
602 666
 			delete modbusdevices;
603 667
 			delete dataqsdkdevices;
668
+			delete jsdevices;
669
+			delete pdevices;
604 670
             window.saveSizeAndPosition("window");
605
-            vsplit.saveState("script/mainSplitter");
606
-            isplit.saveState("script/instrumentSplitter");
607
-            lsplit.saveState("script/logSplitter");
671
+            vsplit.saveState("script/loggingView/" + roasterIndex + "/mainSplitter");
672
+			QSettings.setValue("script/loggingView/" + roasterIndex + "/instrumentCount", isplit.count());
673
+            isplit.saveState("script/loggingView/" + roasterIndex + "/instrumentSplitter");
674
+            lsplit.saveState("script/loggingView/" + roasterIndex + "/logSplitter");
608 675
             log.saveState("script/log", 7);
609 676
 			window.navigationWindow.loggingWindow = undefined;
677
+			Windows.loggingWindow = undefined;
610 678
         });
611 679
 		this.restoreSizeAndPosition('window');
612
-        vsplit.restoreState("script/mainSplitter");
613
-        isplit.restoreState("script/instrumentSplitter");
614
-        lsplit.restoreState("script/logSplitter");
680
+        vsplit.restoreState("script/loggingView/" + roasterIndex + "/mainSplitter");
681
+		if(QSettings.value("script/loggingView/" + roasterIndex + "/instrumentCount") == isplit.count()) {
682
+			isplit.restoreState("script/loggingView/" + roasterIndex + "/instrumentSplitter");
683
+		}
684
+        lsplit.restoreState("script/loggingView/" + roasterIndex + "/logSplitter");
615 685
 		log.restoreState("script/log", 7);
616 686
 		var offsets = new Array();
617 687
 		var zeroemitters = new Array();
@@ -851,7 +921,11 @@
851 921
                     QSettings.setValue("liveColumn", c + 1);
852 922
 					window.postLoadColumnSetup(c);
853 923
 				});
924
+				graph.updatesEnabled = false;
925
+				log.updatesEnabled = false;
854 926
                 input.input();
927
+				log.updatesEnabled = true;
928
+				graph.updatesEnabled = true;
855 929
                 window.windowTitle = 'Typica - [*]' + baseName(filename);
856 930
                 QSettings.setValue("script/lastDir", dir(filename));
857 931
 				log.newAnnotation("End", 1, lc);
@@ -948,6 +1022,7 @@
948 1022
 			var bwindow = createWindow("sampleRoastingBatch");
949 1023
 		});
950 1024
 		window.postLoadColumnSetup(0);
1025
+		Windows.loggingWindow = window;
951 1026
 		]]>
952 1027
     </program>
953 1028
 </window>

+ 1
- 1
config/Windows/profilehistory.xml View File

@@ -51,7 +51,7 @@
51 51
 		var currentProfileID;
52 52
 		setCurrent.clicked.connect(function() {
53 53
 			var query = new QSqlQuery();
54
-			query.prepare("INSERT INTO item_files VALUES('now', :coffee, :profile)");
54
+			query.prepare("INSERT INTO item_files (time, item, files) VALUES('now', :coffee, :profile)");
55 55
 			query.bind(":coffee", coffeelist.currentData());
56 56
 			query.bind(":profile", "{" + currentProfileID + "}");
57 57
 			query.exec();

+ 8
- 8
config/Windows/purchase.xml View File

@@ -203,7 +203,7 @@
203 203
             var dateField = findChildObject(this, 'date');
204 204
             submitButton.clicked.connect(function() {
205 205
                 var query = new QSqlQuery;
206
-                var q = "INSERT INTO invoices VALUES (default, :invoice, :vendor, :date) RETURNING id";
206
+                var q = "INSERT INTO invoices (id, invoice, vendor, time) VALUES (default, :invoice, :vendor, :date) RETURNING id";
207 207
                 query.prepare(q);
208 208
                 query.bind(":invoice", invoiceField.text);
209 209
                 query.bind(":vendor", vendorField.currentText);
@@ -246,11 +246,11 @@
246 246
                     }
247 247
                     if(decafSelection.checked)
248 248
                     {
249
-                        q = "INSERT INTO decaf_coffees VALUES (default, :name, :reference, :unit, 0, 'Coffee: Unroasted', :origin, :region, :producer, :grade, :milling, :drying, :decafMethod) RETURNING id";
249
+                        q = "INSERT INTO decaf_coffees (id, name, reference, unit, quantity, category, origin, region, producer, grade, milling, drying, decaf_method) VALUES (default, :name, :reference, :unit, 0, 'Coffee: Unroasted', :origin, :region, :producer, :grade, :milling, :drying, :decafMethod) RETURNING id";
250 250
                     }
251 251
                     else
252 252
                     {
253
-                        q = "INSERT INTO coffees VALUES (default, :name, :reference, :unit, 0, 'Coffee: Unroasted', :origin, :region, :producer, :grade, :milling, :drying) RETURNING id";
253
+                        q = "INSERT INTO coffees (id, name, reference, unit, quantity, category, origin, region, producer, grade, milling, drying) VALUES (default, :name, :reference, :unit, 0, 'Coffee: Unroasted', :origin, :region, :producer, :grade, :milling, :drying) RETURNING id";
254 254
                     }
255 255
                     query.prepare(q);
256 256
                     query.bind(":name", nameEntry.currentText);
@@ -317,7 +317,7 @@
317 317
                     query.exec();
318 318
                     query.next();
319 319
                     var item_id = query.value(0);
320
-                    q = "INSERT INTO purchase VALUES(:time, :item, :quantity, :cost, :vendor)";
320
+                    q = "INSERT INTO purchase (time, item, quantity, cost, vendor) VALUES(:time, :item, :quantity, :cost, :vendor)";
321 321
                     query.prepare(q);
322 322
                     query.bind(":time", dateField.date);
323 323
                     query.bind(":item", item_id);
@@ -333,14 +333,14 @@
333 333
                     }
334 334
                     query.bind(":vendor", vendorField.currentText);
335 335
                     query.exec();
336
-                    q = "INSERT INTO lb_bag_conversion VALUES(:item, :conversion)";
336
+                    q = "INSERT INTO lb_bag_conversion (item, conversion) VALUES(:item, :conversion)";
337 337
                     query.prepare(q);
338 338
                     query.bind(":item", item_id);
339 339
                     var bagsEntry = findChildObject(current, 'bags');
340 340
                     var conversion = convertToPounds(parseFloat(quantityEntry.text), unitEntry.currentText) / Number(bagsEntry.text);
341 341
                     query.bind(":conversion", conversion);
342 342
                     query.exec();
343
-                    q = "INSERT INTO invoice_items VALUES(:id, 'PURCHASE', :item, :description, :cost)";
343
+                    q = "INSERT INTO invoice_items (invoice_id, record_type, item_id, description, cost) VALUES(:id, 'PURCHASE', :item, :description, :cost)";
344 344
                     query.prepare(q);
345 345
                     query.bind(":id", invoiceNumber);
346 346
                     query.bind(":item", item_id);
@@ -356,7 +356,7 @@
356 356
                     query.exec();
357 357
                     var certifications = findChildObject(current, 'certdisplay');
358 358
                     var certlist = certifications.text.split(",");
359
-                    q = "INSERT INTO certifications VALUES (:item, :certification)";
359
+                    q = "INSERT INTO certifications (item, certification) VALUES (:item, :certification)";
360 360
                     query.prepare(q);
361 361
                     query.bind(":item", item_id);
362 362
                     for(var j = 0; j < certlist.length; j++)
@@ -366,7 +366,7 @@
366 366
                     }
367 367
                 }
368 368
                 var descriptionArray = sqlToArray(feesTable.columnArray(0, 0));
369
-                var q = "INSERT INTO invoice_items VALUES (:id, 'FEE', NULL, :description, :cost)";
369
+                var q = "INSERT INTO invoice_items (invoice_id, record_type, item_id, description, cost) VALUES (:id, 'FEE', NULL, :description, :cost)";
370 370
                 query.prepare(q);
371 371
                 query.bind(":id", invoiceNumber);
372 372
                 if(descriptionArray.length > 0)

+ 3
- 3
config/Windows/roastmanager.xml View File

@@ -46,7 +46,7 @@
46 46
                 window.displayStatus("Item removed.");
47 47
             });
48 48
             restoreButton.clicked.connect(function() {
49
-                var q = "INSERT INTO current_items VALUES (:id)";
49
+                var q = "INSERT INTO current_items (item) VALUES (:id)";
50 50
                 query = new QSqlQuery();
51 51
                 query.prepare(q);
52 52
                 query.bind(":id", drop2.currentData());
@@ -59,14 +59,14 @@
59 59
                 window.displayStatus("Item restored.");
60 60
             });
61 61
             newItemButton.clicked.connect(function() {
62
-                var q = "INSERT INTO items VALUES (default, :name, NULL, 'lb', 0, 'Coffee: Roasted') RETURNING id";
62
+                var q = "INSERT INTO items (id, name, reference, unit, quantity, category) VALUES (default, :name, NULL, 'lb', 0, 'Coffee: Roasted') RETURNING id";
63 63
                 query = new QSqlQuery();
64 64
                 query.prepare(q);
65 65
                 query.bind(":name", itemname.text);
66 66
                 query.exec();
67 67
                 query.next();
68 68
                 var i = query.value(0);
69
-                q = "INSERT INTO current_items VALUES(:id)";
69
+                q = "INSERT INTO current_items (item) VALUES(:id)";
70 70
                 query.prepare(q);
71 71
                 query.bind(":id", i);
72 72
                 query.exec();

+ 3
- 0
config/config.xml View File

@@ -34,6 +34,8 @@
34 34
 	<include src="Windows/editbatchdetails.xml" />
35 35
 	<include src="Windows/newsamplebatch.xml" />
36 36
 	<program>
37
+		<![CDATA[
38
+		Windows = new Object();
37 39
 		var loggingWindow;
38 40
 		var currentBatchInfo;
39 41
 		var navwindow = createWindow("navwindow");
@@ -42,5 +44,6 @@
42 44
 		var azero;
43 45
 		var bzero;
44 46
 		navwindow.windowTitle = "Typica - Choose Your Path";
47
+		]]>
45 48
 	</program>
46 49
 </application>

+ 5
- 5
src/abouttypica.cpp View File

@@ -1,9 +1,9 @@
1
-/*222:*/
1
+/*260:*/
2 2
 #line 33 "./abouttypica.w"
3 3
 
4 4
 #include "abouttypica.h"
5 5
 
6
-/*223:*/
6
+/*261:*/
7 7
 #line 42 "./abouttypica.w"
8 8
 
9 9
 AboutTypica::AboutTypica():QMainWindow(NULL)
@@ -17,10 +17,10 @@ aboutFile.close();
17 17
 setCentralWidget(banner);
18 18
 }
19 19
 
20
-#line 5641 "./typica.w"
20
+#line 6340 "./typica.w"
21 21
 
22
-/*:223*/
22
+/*:261*/
23 23
 #line 36 "./abouttypica.w"
24 24
 
25 25
 
26
-/*:222*/
26
+/*:260*/

+ 2
- 2
src/abouttypica.h View File

@@ -1,4 +1,4 @@
1
-/*221:*/
1
+/*259:*/
2 2
 #line 14 "./abouttypica.w"
3 3
 
4 4
 #include <QMainWindow> 
@@ -17,4 +17,4 @@ AboutTypica();
17 17
 
18 18
 #endif
19 19
 
20
-/*:221*/
20
+/*:259*/

+ 61
- 0
src/advancedsettings.w View File

@@ -0,0 +1,61 @@
1
+@* Advanced settings configuration.
2
+
3
+\noindent Sometimes a feature has a sensible default that should be used the
4
+vast majority of the time but sometimes requires some other setting to be
5
+available in a reasonably accessible way.
6
+
7
+@<Class declarations@>=
8
+class AdvancedSettingsWidget : public QWidget@/
9
+{@/
10
+	@[Q_OBJECT@]@;
11
+	public:@/
12
+		AdvancedSettingsWidget();
13
+	@[public slots:@]@/
14
+		void enableDiagnosticLogging(bool enabled);
15
+};
16
+
17
+@ At present the advanced settings consist only of an option to redirect
18
+diagnostic output to a file. This should normally be disabled as it supresses
19
+console output and there is no mechanism within \pn{} for periodically removing
20
+the files generated. It is, however, useful for producing a diagnostic file
21
+that can be attached to an email if someone is encountering an issue that they
22
+are not able to resolve on their own. It is especially useful on Microsoft
23
+Windows where this output is not otherwise available unless Typica is run from
24
+software development tools most people do not have installed.
25
+
26
+@<AdvancedSettingsWidget implementation@>=
27
+AdvancedSettingsWidget::AdvancedSettingsWidget() : QWidget(NULL)
28
+{
29
+	QSettings settings;
30
+	QFormLayout *layout = new QFormLayout;
31
+	QCheckBox *logDiagnostics = new QCheckBox;
32
+	logDiagnostics->setCheckState(
33
+		settings.value("settings/advanced/logging", false).toBool() ?
34
+		Qt::Checked : Qt::Unchecked);
35
+	connect(logDiagnostics, SIGNAL(toggled(bool)), this, SLOT(enableDiagnosticLogging(bool)));
36
+	layout->addRow(tr("Enable diagnostic logging"), logDiagnostics);
37
+	setLayout(layout);
38
+}
39
+
40
+@ Changes to this setting should take effect immediately. It should also be
41
+written to |QSettings| so the feature can be correctly enabled or not. 
42
+
43
+@<AdvancedSettingsWidget implementation@>=
44
+void AdvancedSettingsWidget::enableDiagnosticLogging(bool enabled)
45
+{
46
+	QSettings settings;
47
+	settings.setValue("settings/advanced/logging", enabled);
48
+	if(enabled)
49
+	{
50
+		qInstallMsgHandler(messageFileOutput);
51
+	}
52
+	else
53
+	{
54
+		qInstallMsgHandler(0);
55
+	}
56
+}
57
+
58
+@ Currently the implementation is brought into typica.cpp.
59
+
60
+@<Class implementations@>=
61
+@<AdvancedSettingsWidget implementation@>

+ 1
- 1
src/dataqsdk.w View File

@@ -339,7 +339,7 @@ void DataqSdkDeviceImplementation::run()
339 339
 	sampleRate = di_sample_rate(sampleRate * channels * 40, &oversample,
340 340
 	                            &burstDivisor);
341 341
 	buffer = new qint16[(int)sampleRate];
342
-	di_inlist_struct inlist[16] = {{0}};
342
+	di_inlist_struct inlist[16] = {{0, 0, 0, 0, 0, 0, 0, 0}};
343 343
 	for(unsigned short i = 0; i < channels; i++)
344 344
 	{
345 345
 		inlist[i].chan = i;

+ 37
- 62
src/daterangeselector.cpp View File

@@ -1,5 +1,5 @@
1
-/*603:*/
2
-#line 66 "./daterangeselector.w"
1
+/*650:*/
2
+#line 68 "./daterangeselector.w"
3 3
 
4 4
 #include <QCalendarWidget> 
5 5
 #include <QPushButton> 
@@ -11,33 +11,8 @@
11 11
 
12 12
 #include "daterangeselector.h"
13 13
 
14
-/*604:*/
15
-#line 86 "./daterangeselector.w"
16
-
17
-class CustomDateRangePopup:public QWidget
18
-{
19
-Q_OBJECT
20
-public:
21
-CustomDateRangePopup(QWidget*parent= NULL);
22
-public slots:
23
-void applyRange();
24
-signals:
25
-void hidingPopup();
26
-protected:
27
-virtual void hideEvent(QHideEvent*event);
28
-private slots:
29
-void validateRange();
30
-private:
31
-QCalendarWidget*startDateSelector;
32
-QCalendarWidget*endDateSelector;
33
-QPushButton*applyButton;
34
-};
35
-
36
-/*:604*/
37
-#line 77 "./daterangeselector.w"
38
-
39
-/*605:*/
40
-#line 110 "./daterangeselector.w"
14
+/*652:*/
15
+#line 111 "./daterangeselector.w"
41 16
 
42 17
 CustomDateRangePopup::CustomDateRangePopup(QWidget*parent):
43 18
 QWidget(parent,Qt::Popup),startDateSelector(new QCalendarWidget),
@@ -80,16 +55,16 @@ outerLayout->addLayout(buttonLayout);
80 55
 setLayout(outerLayout);
81 56
 }
82 57
 
83
-/*:605*//*606:*/
84
-#line 158 "./daterangeselector.w"
58
+/*:652*//*653:*/
59
+#line 159 "./daterangeselector.w"
85 60
 
86 61
 void CustomDateRangePopup::hideEvent(QHideEvent*)
87 62
 {
88 63
 emit hidingPopup();
89 64
 }
90 65
 
91
-/*:606*//*607:*/
92
-#line 167 "./daterangeselector.w"
66
+/*:653*//*654:*/
67
+#line 168 "./daterangeselector.w"
93 68
 
94 69
 void CustomDateRangePopup::applyRange()
95 70
 {
@@ -103,8 +78,8 @@ endDateSelector->selectedDate().toString(Qt::ISODate)));
103 78
 hide();
104 79
 }
105 80
 
106
-/*:607*//*608:*/
107
-#line 184 "./daterangeselector.w"
81
+/*:654*//*655:*/
82
+#line 185 "./daterangeselector.w"
108 83
 
109 84
 void CustomDateRangePopup::validateRange()
110 85
 {
@@ -118,11 +93,11 @@ applyButton->setEnabled(true);
118 93
 }
119 94
 }
120 95
 
121
-/*:608*/
122
-#line 78 "./daterangeselector.w"
96
+/*:655*/
97
+#line 79 "./daterangeselector.w"
123 98
 
124
-/*609:*/
125
-#line 202 "./daterangeselector.w"
99
+/*656:*/
100
+#line 203 "./daterangeselector.w"
126 101
 
127 102
 DateRangeSelector::DateRangeSelector(QWidget*parent):
128 103
 QWidget(parent),quickSelector(new QComboBox(this)),
@@ -133,8 +108,8 @@ connect(quickSelector,SIGNAL(currentIndexChanged(int)),this,SLOT(updateRange(int
133 108
 QDate currentDate= QDate::currentDate();
134 109
 
135 110
 QHBoxLayout*layout= new QHBoxLayout;
136
-/*610:*/
137
-#line 231 "./daterangeselector.w"
111
+/*657:*/
112
+#line 232 "./daterangeselector.w"
138 113
 
139 114
 quickSelector->addItem("Yesterday",QVariant(QStringList()<<
140 115
 currentDate.addDays(-1).toString(Qt::ISODate)));
@@ -213,8 +188,8 @@ quickSelector->insertSeparator(quickSelector->count());
213 188
 quickSelector->addItem("Lifetime");
214 189
 quickSelector->addItem("Custom");
215 190
 
216
-/*:610*/
217
-#line 212 "./daterangeselector.w"
191
+/*:657*/
192
+#line 213 "./daterangeselector.w"
218 193
 
219 194
 QToolButton*customButton= new QToolButton;
220 195
 customButton->setIcon(QIcon::fromTheme("office-calendar",
@@ -226,8 +201,8 @@ setLayout(layout);
226 201
 connect(customButton,SIGNAL(clicked()),this,SLOT(toggleCustom()));
227 202
 }
228 203
 
229
-/*:609*//*611:*/
230
-#line 314 "./daterangeselector.w"
204
+/*:656*//*658:*/
205
+#line 315 "./daterangeselector.w"
231 206
 
232 207
 void DateRangeSelector::updateRange(int index)
233 208
 {
@@ -242,8 +217,8 @@ emit rangeUpdated(quickSelector->itemData(quickSelector->currentIndex()));
242 217
 }
243 218
 }
244 219
 
245
-/*:611*//*612:*/
246
-#line 331 "./daterangeselector.w"
220
+/*:658*//*659:*/
221
+#line 332 "./daterangeselector.w"
247 222
 
248 223
 void DateRangeSelector::popupHidden()
249 224
 {
@@ -252,8 +227,8 @@ customRangeSelector= NULL;
252 227
 quickSelector->setCurrentIndex(lastIndex);
253 228
 }
254 229
 
255
-/*:612*//*613:*/
256
-#line 342 "./daterangeselector.w"
230
+/*:659*//*660:*/
231
+#line 343 "./daterangeselector.w"
257 232
 
258 233
 void DateRangeSelector::setCustomRange(QVariant range)
259 234
 {
@@ -263,8 +238,8 @@ lastIndex= quickSelector->count()-1;
263 238
 quickSelector->setCurrentIndex(lastIndex);
264 239
 }
265 240
 
266
-/*:613*//*614:*/
267
-#line 357 "./daterangeselector.w"
241
+/*:660*//*661:*/
242
+#line 358 "./daterangeselector.w"
268 243
 
269 244
 void DateRangeSelector::toggleCustom()
270 245
 {
@@ -304,24 +279,24 @@ customRangeSelector= NULL;
304 279
 }
305 280
 }
306 281
 
307
-/*:614*//*615:*/
308
-#line 399 "./daterangeselector.w"
282
+/*:661*//*662:*/
283
+#line 400 "./daterangeselector.w"
309 284
 
310 285
 QVariant DateRangeSelector::currentRange()
311 286
 {
312 287
 return quickSelector->itemData(lastIndex);
313 288
 }
314 289
 
315
-/*:615*//*616:*/
316
-#line 407 "./daterangeselector.w"
290
+/*:662*//*663:*/
291
+#line 408 "./daterangeselector.w"
317 292
 
318 293
 void DateRangeSelector::setCurrentIndex(int index)
319 294
 {
320 295
 quickSelector->setCurrentIndex(index);
321 296
 }
322 297
 
323
-/*:616*//*617:*/
324
-#line 422 "./daterangeselector.w"
298
+/*:663*//*664:*/
299
+#line 423 "./daterangeselector.w"
325 300
 
326 301
 void DateRangeSelector::setLifetimeRange(QString startDate,QString endDate)
327 302
 {
@@ -329,18 +304,18 @@ quickSelector->setItemData(quickSelector->count()-2,
329 304
 QVariant(QStringList()<<startDate<<endDate));
330 305
 }
331 306
 
332
-/*:617*//*618:*/
333
-#line 432 "./daterangeselector.w"
307
+/*:664*//*665:*/
308
+#line 433 "./daterangeselector.w"
334 309
 
335 310
 void DateRangeSelector::removeIndex(int index)
336 311
 {
337 312
 quickSelector->removeItem(index);
338 313
 }
339 314
 
340
-/*:618*/
341
-#line 79 "./daterangeselector.w"
315
+/*:665*/
316
+#line 80 "./daterangeselector.w"
342 317
 
343 318
 
344 319
 #include "moc_daterangeselector.cpp"
345 320
 
346
-/*:603*/
321
+/*:650*/

+ 29
- 3
src/daterangeselector.h View File

@@ -1,13 +1,39 @@
1
-/*602:*/
1
+/*649:*/
2 2
 #line 30 "./daterangeselector.w"
3 3
 
4 4
 
5 5
 #include <QComboBox> 
6
+#include <QPushButton> 
7
+#include <QCalendarWidget> 
6 8
 
7 9
 #ifndef TypicaDateRangeSelectorHeader
8 10
 #define TypicaDateRangeSelectorHeader
9 11
 
10
-class CustomDateRangePopup;
12
+/*651:*/
13
+#line 87 "./daterangeselector.w"
14
+
15
+class CustomDateRangePopup:public QWidget
16
+{
17
+Q_OBJECT
18
+public:
19
+CustomDateRangePopup(QWidget*parent= NULL);
20
+public slots:
21
+void applyRange();
22
+signals:
23
+void hidingPopup();
24
+protected:
25
+virtual void hideEvent(QHideEvent*event);
26
+private slots:
27
+void validateRange();
28
+private:
29
+QCalendarWidget*startDateSelector;
30
+QCalendarWidget*endDateSelector;
31
+QPushButton*applyButton;
32
+};
33
+
34
+/*:651*/
35
+#line 39 "./daterangeselector.w"
36
+
11 37
 
12 38
 class DateRangeSelector:public QWidget
13 39
 {
@@ -34,4 +60,4 @@ int lastIndex;
34 60
 
35 61
 #endif
36 62
 
37
-/*:602*/
63
+/*:649*/

+ 3
- 2
src/daterangeselector.w View File

@@ -30,11 +30,13 @@ access to the ISO 8601 string representation of these dates is provided.
30 30
 @(daterangeselector.h@>=
31 31
 
32 32
 #include <QComboBox>
33
+#include <QPushButton>
34
+#include <QCalendarWidget>
33 35
 
34 36
 #ifndef TypicaDateRangeSelectorHeader
35 37
 #define TypicaDateRangeSelectorHeader
36 38
 
37
-class CustomDateRangePopup;
39
+@<CustomDateRangePopup declaration@>@;
38 40
 
39 41
 class DateRangeSelector : public QWidget
40 42
 {
@@ -74,7 +76,6 @@ class DateRangeSelector : public QWidget
74 76
 
75 77
 #include "daterangeselector.h"
76 78
 
77
-@<CustomDateRangePopup declaration@>
78 79
 @<CustomDateRangePopup implementation@>
79 80
 @<DateRangeSelector implementation@>
80 81
 

+ 2
- 2
src/draglabel.cpp View File

@@ -1,4 +1,4 @@
1
-/*863:*/
1
+/*972:*/
2 2
 #line 33 "./scales.w"
3 3
 
4 4
 #include "draglabel.h"
@@ -26,4 +26,4 @@ drag->exec();
26 26
 }
27 27
 }
28 28
 
29
-/*:863*/
29
+/*:972*/

+ 2
- 2
src/draglabel.h View File

@@ -1,4 +1,4 @@
1
-/*862:*/
1
+/*971:*/
2 2
 #line 13 "./scales.w"
3 3
 
4 4
 #ifndef TypicaDragLabelInclude
@@ -17,4 +17,4 @@ void mousePressEvent(QMouseEvent*event);
17 17
 
18 18
 #endif
19 19
 
20
-/*:862*/
20
+/*:971*/

+ 127
- 0
src/feedback.w View File

@@ -0,0 +1,127 @@
1
+@* The Feedback Wizard.
2
+
3
+\noindent While the About Typica window includes a link for sending email, this
4
+is not as obvious as a Send Feedback option from the Help menu. It also does
5
+not provide an adequate amount of prompting for feedback to be useful.
6
+
7
+This functionality is provided in the form of a |QWizard| and five
8
+|QWizardPage|s to guide people through the feedback process.
9
+
10
+@(feedback.h@>=
11
+#include <QWizard>
12
+#include <QComboBox>
13
+#include <QTextEdit>
14
+#include <QLabel>
15
+
16
+#ifndef FeedbackHeader
17
+#define FeedbackHeader
18
+
19
+class FeedbackWizard : public QWizard
20
+{
21
+	Q_OBJECT
22
+	public:
23
+		FeedbackWizard();
24
+	private slots:
25
+		void setCommentInstructions(int index);
26
+	private:
27
+		QComboBox *feedbackType;
28
+		QLabel *commentInstructions;
29
+		QTextEdit *comment;
30
+};
31
+
32
+#endif
33
+
34
+@ Implementation is in a separate file.
35
+
36
+@(feedback.cpp@>=
37
+#include "feedback.h"
38
+#include <QLabel>
39
+#include <QVBoxLayout>
40
+#include <QWizardPage>
41
+
42
+@<Feedback wizard implementation@>@;
43
+
44
+@ The feedback wizard constructor is responsible for setting up its appearance,
45
+adding pages, and ensuring connections for properly responding to input are set
46
+up.
47
+
48
+@<Feedback wizard implementation@>=
49
+FeedbackWizard::FeedbackWizard() : QWizard()
50
+{
51
+	@<Set wizard graphics@>@;
52
+	@<Set feedback wizard pages@>@;
53
+}
54
+
55
+@ At present we do not have any custom graphics for wizards. If we did, they
56
+would go here.
57
+
58
+@<Set wizard graphics@>=
59
+/* Nothing needs to be done here. */
60
+
61
+@ The first page presented is an introduction page where we would like to know
62
+what sort of feedback is going to be provided. This is selected in a
63
+|QComboBox| which wil be used to update information on later pages.
64
+
65
+@<Set feedback wizard pages@>=
66
+QWizardPage *introPage = new QWizardPage;
67
+introPage->setTitle(tr("Send Feedback"));
68
+introPage->setSubTitle("Select the type of feedback you wish to provide");
69
+setPixmap(QWizard::LogoPixmap, QPixmap(":/resources/icons/appicons/logo48.png"));
70
+QLabel *page1prompt = new QLabel(tr("What sort of feedback would you like to provide?"));
71
+feedbackType = new QComboBox;
72
+feedbackType->addItem(tr("Bug Report"));
73
+feedbackType->addItem(tr("Comment"));
74
+feedbackType->addItem(tr("Feature Request"));
75
+feedbackType->addItem(tr("Question"));
76
+QVBoxLayout *page1layout = new QVBoxLayout;
77
+page1layout->addStretch();
78
+page1layout->addWidget(page1prompt);
79
+page1layout->addWidget(feedbackType);
80
+page1layout->addStretch();
81
+introPage->setLayout(page1layout);
82
+addPage(introPage);
83
+connect(feedbackType, SIGNAL(currentIndexChanged(int)), this, SLOT(setCommentInstructions(int)));
84
+commentInstructions = new QLabel;
85
+commentInstructions->setWordWrap(true);
86
+setCommentInstructions(0);
87
+
88
+@ The selection on the first page influences the instructions provided on the
89
+second page.
90
+
91
+@<Feedback wizard implementation@>=
92
+void FeedbackWizard::setCommentInstructions(int index)
93
+{
94
+	switch(index)
95
+	{
96
+		case 0:
97
+			commentInstructions->setText(tr("Please provide as complete a description of the issue as possible. Useful information includes step by step instructions for replicating the issue, the resulting behavior, and what you expected."));
98
+			break;
99
+		case 1:
100
+			commentInstructions->setText(tr("If you would like action to occur based on your comment, please be specific."));
101
+			break;
102
+		case 2:
103
+			commentInstructions->setText(tr("Please try to be as clear as possible about what you want, where you believe this would fit in with what is already provided, and provide a description of why this would be useful."));
104
+			break;
105
+		case 3:
106
+			commentInstructions->setText(tr("Please be specific and provide any information that you think might be useful in answering your question."));
107
+			break;
108
+		default:
109
+			commentInstructions->setText("");
110
+			break;
111
+	}
112
+}
113
+
114
+@ The second page provides both guidance on writing feedback and an area for typing this.
115
+
116
+@<Set feedback wizard pages@>=
117
+QWizardPage *commentPage = new QWizardPage;
118
+commentPage->setTitle(tr("Send Feedback"));
119
+commentPage->setSubTitle(tr("Your feedback is appreciated"));
120
+comment = new QTextEdit;
121
+QVBoxLayout *page2layout = new QVBoxLayout;
122
+page2layout->addStretch();
123
+page2layout->addWidget(commentInstructions);
124
+page2layout->addWidget(comment);
125
+page2layout->addStretch();
126
+commentPage->setLayout(page2layout);
127
+addPage(commentPage);

+ 7
- 6
src/helpmenu.cpp View File

@@ -1,10 +1,10 @@
1
-/*163:*/
1
+/*194:*/
2 2
 #line 35 "./helpmenu.w"
3 3
 
4 4
 #include "helpmenu.h"
5 5
 #include "abouttypica.h"
6 6
 
7
-/*164:*/
7
+/*195:*/
8 8
 #line 47 "./helpmenu.w"
9 9
 
10 10
 HelpMenu::HelpMenu():QMenu()
@@ -17,7 +17,7 @@ addAction(aboutTypicaAction);
17 17
 connect(aboutTypicaAction,SIGNAL(triggered()),this,SLOT(displayAboutTypica()));
18 18
 }
19 19
 
20
-/*:164*//*165:*/
20
+/*:195*//*196:*/
21 21
 #line 61 "./helpmenu.w"
22 22
 
23 23
 void HelpMenu::displayAboutTypica()
@@ -26,10 +26,11 @@ AboutTypica*aboutBox= new AboutTypica;
26 26
 aboutBox->show();
27 27
 }
28 28
 
29
-#line 3946 "./typica.w"
29
+#line 4614 "./typica.w"
30 30
 
31
-/*:165*/
31
+#line 1 "./feedback.w"
32
+/*:196*/
32 33
 #line 39 "./helpmenu.w"
33 34
 
34 35
 
35
-/*:163*/
36
+/*:194*/

+ 2
- 2
src/helpmenu.h View File

@@ -1,4 +1,4 @@
1
-/*162:*/
1
+/*193:*/
2 2
 #line 16 "./helpmenu.w"
3 3
 
4 4
 #include <QMenu> 
@@ -17,4 +17,4 @@ void displayAboutTypica();
17 17
 
18 18
 #endif
19 19
 
20
-/*:162*/
20
+/*:193*/

+ 72
- 1
src/measurement.w View File

@@ -121,4 +121,75 @@ public default constructor (already defined above), a public copy constructor,
121 121
 and a public destructor. These latter two are default generated.
122 122
 
123 123
 @<Register meta-types@>=
124
-qRegisterMetaType<Measurement>("Measurement");
124
+qRegisterMetaType<Measurement>("Measurement");
125
+
126
+@ A little more is required to use |Measurement| objects in scripts.
127
+
128
+@<Class declarations@>=
129
+Q_DECLARE_METATYPE(Measurement)
130
+
131
+@ The only thing unusual here is the conversion to and from script values.
132
+
133
+@<Function prototypes for scripting@>=
134
+QScriptValue constructMeasurement(QScriptContext *context, QScriptEngine *engine);
135
+void setMeasurementProperties(QScriptValue value, QScriptEngine *engine);
136
+QScriptValue Measurement_toScriptValue(QScriptEngine *engine, const Measurement &measurement);
137
+void Measurement_fromScriptValue(const QScriptValue &value, Measurement &measurement);
138
+
139
+@ This follows much the same pattern as other types not derived from |QObject|.
140
+
141
+@<Set up the scripting engine@>=
142
+constructor = engine->newFunction(constructMeasurement);
143
+engine->globalObject().setProperty("Measurement", constructor);
144
+qScriptRegisterMetaType(engine, Measurement_toScriptValue, Measurement_fromScriptValue);
145
+
146
+@ The constructor takes two or three arguments. If the third arguement is not
147
+supplied, we will assume that the measurements are in degrees Fahrenheit.
148
+
149
+@<Functions for scripting@>=
150
+QScriptValue constructMeasurement(QScriptContext *context, QScriptEngine *engine)
151
+{
152
+	QScriptValue object;
153
+	if(context->argumentCount() == 2 || context->argumentCount() == 3)
154
+	{
155
+		double measurement = argument<double>(0, context);
156
+		QTime timestamp = argument<QTime>(1, context);
157
+		Units::Unit unit = Units::Fahrenheit;
158
+		if(context->argumentCount() == 3)
159
+		{
160
+			unit = argument<Units::Unit>(2, context);
161
+		}
162
+		object = engine->toScriptValue<Measurement>(Measurement(measurement, timestamp, unit));
163
+		setMeasurementProperties(object, engine);
164
+	}
165
+	else
166
+	{
167
+		context->throwError("Incorrect number of arguments passed to "@|
168
+		                    "Measurement::Measurement(). This method takes two "@|
169
+		                    "or three arguments.");
170
+	}
171
+	return object;
172
+}
173
+
174
+@ No additional properties are currently needed, but if they were, they would go here.
175
+
176
+@<Functions for scripting@>=
177
+void setMeasurementProperties(QScriptValue, QScriptEngine *)
178
+{
179
+	/* Nothing needs to be done here. */
180
+}
181
+
182
+@ The script value conversions are reasonably straightforward.
183
+
184
+@<Functions for scripting@>=
185
+QScriptValue Measurement_toScriptValue(QScriptEngine *engine, const Measurement &measurement)
186
+{
187
+	QVariant var;
188
+	var.setValue(measurement);
189
+	return engine->newVariant(var);
190
+}
191
+
192
+void Measurement_fromScriptValue(const QScriptValue &value, Measurement &measurement)
193
+{
194
+	measurement = value.toVariant().value<Measurement>();
195
+}

+ 80
- 0
src/moc_abouttypica.cpp View File

@@ -0,0 +1,80 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'abouttypica.h'
3
+**
4
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6)
5
+**
6
+** WARNING! All changes made in this file will be lost!
7
+*****************************************************************************/
8
+
9
+#include "abouttypica.h"
10
+#if !defined(Q_MOC_OUTPUT_REVISION)
11
+#error "The header file 'abouttypica.h' doesn't include <QObject>."
12
+#elif Q_MOC_OUTPUT_REVISION != 63
13
+#error "This file was generated using the moc from 4.8.6. It"
14
+#error "cannot be used with the include files from this version of Qt."
15
+#error "(The moc has changed too much.)"
16
+#endif
17
+
18
+QT_BEGIN_MOC_NAMESPACE
19
+static const uint qt_meta_data_AboutTypica[] = {
20
+
21
+ // content:
22
+       6,       // revision
23
+       0,       // classname
24
+       0,    0, // classinfo
25
+       0,    0, // methods
26
+       0,    0, // properties
27
+       0,    0, // enums/sets
28
+       0,    0, // constructors
29
+       0,       // flags
30
+       0,       // signalCount
31
+
32
+       0        // eod
33
+};
34
+
35
+static const char qt_meta_stringdata_AboutTypica[] = {
36
+    "AboutTypica\0"
37
+};
38
+
39
+void AboutTypica::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
40
+{
41
+    Q_UNUSED(_o);
42
+    Q_UNUSED(_id);
43
+    Q_UNUSED(_c);
44
+    Q_UNUSED(_a);
45
+}
46
+
47
+const QMetaObjectExtraData AboutTypica::staticMetaObjectExtraData = {
48
+    0,  qt_static_metacall 
49
+};
50
+
51
+const QMetaObject AboutTypica::staticMetaObject = {
52
+    { &QMainWindow::staticMetaObject, qt_meta_stringdata_AboutTypica,
53
+      qt_meta_data_AboutTypica, &staticMetaObjectExtraData }
54
+};
55
+
56
+#ifdef Q_NO_DATA_RELOCATION
57
+const QMetaObject &AboutTypica::getStaticMetaObject() { return staticMetaObject; }
58
+#endif //Q_NO_DATA_RELOCATION
59
+
60
+const QMetaObject *AboutTypica::metaObject() const
61
+{
62
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
63
+}
64
+
65
+void *AboutTypica::qt_metacast(const char *_clname)
66
+{
67
+    if (!_clname) return 0;
68
+    if (!strcmp(_clname, qt_meta_stringdata_AboutTypica))
69
+        return static_cast<void*>(const_cast< AboutTypica*>(this));
70
+    return QMainWindow::qt_metacast(_clname);
71
+}
72
+
73
+int AboutTypica::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
74
+{
75
+    _id = QMainWindow::qt_metacall(_c, _id, _a);
76
+    if (_id < 0)
77
+        return _id;
78
+    return _id;
79
+}
80
+QT_END_MOC_NAMESPACE

+ 210
- 0
src/moc_daterangeselector.cpp View File

@@ -0,0 +1,210 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'daterangeselector.h'
3
+**
4
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6)
5
+**
6
+** WARNING! All changes made in this file will be lost!
7
+*****************************************************************************/
8
+
9
+#include "daterangeselector.h"
10
+#if !defined(Q_MOC_OUTPUT_REVISION)
11
+#error "The header file 'daterangeselector.h' doesn't include <QObject>."
12
+#elif Q_MOC_OUTPUT_REVISION != 63
13
+#error "This file was generated using the moc from 4.8.6. It"
14
+#error "cannot be used with the include files from this version of Qt."
15
+#error "(The moc has changed too much.)"
16
+#endif
17
+
18
+QT_BEGIN_MOC_NAMESPACE
19
+static const uint qt_meta_data_CustomDateRangePopup[] = {
20
+
21
+ // content:
22
+       6,       // revision
23
+       0,       // classname
24
+       0,    0, // classinfo
25
+       3,   14, // methods
26
+       0,    0, // properties
27
+       0,    0, // enums/sets
28
+       0,    0, // constructors
29
+       0,       // flags
30
+       1,       // signalCount
31
+
32
+ // signals: signature, parameters, type, tag, flags
33
+      22,   21,   21,   21, 0x05,
34
+
35
+ // slots: signature, parameters, type, tag, flags
36
+      36,   21,   21,   21, 0x0a,
37
+      49,   21,   21,   21, 0x08,
38
+
39
+       0        // eod
40
+};
41
+
42
+static const char qt_meta_stringdata_CustomDateRangePopup[] = {
43
+    "CustomDateRangePopup\0\0hidingPopup()\0"
44
+    "applyRange()\0validateRange()\0"
45
+};
46
+
47
+void CustomDateRangePopup::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
48
+{
49
+    if (_c == QMetaObject::InvokeMetaMethod) {
50
+        Q_ASSERT(staticMetaObject.cast(_o));
51
+        CustomDateRangePopup *_t = static_cast<CustomDateRangePopup *>(_o);
52
+        switch (_id) {
53
+        case 0: _t->hidingPopup(); break;
54
+        case 1: _t->applyRange(); break;
55
+        case 2: _t->validateRange(); break;
56
+        default: ;
57
+        }
58
+    }
59
+    Q_UNUSED(_a);
60
+}
61
+
62
+const QMetaObjectExtraData CustomDateRangePopup::staticMetaObjectExtraData = {
63
+    0,  qt_static_metacall 
64
+};
65
+
66
+const QMetaObject CustomDateRangePopup::staticMetaObject = {
67
+    { &QWidget::staticMetaObject, qt_meta_stringdata_CustomDateRangePopup,
68
+      qt_meta_data_CustomDateRangePopup, &staticMetaObjectExtraData }
69
+};
70
+
71
+#ifdef Q_NO_DATA_RELOCATION
72
+const QMetaObject &CustomDateRangePopup::getStaticMetaObject() { return staticMetaObject; }
73
+#endif //Q_NO_DATA_RELOCATION
74
+
75
+const QMetaObject *CustomDateRangePopup::metaObject() const
76
+{
77
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
78
+}
79
+
80
+void *CustomDateRangePopup::qt_metacast(const char *_clname)
81
+{
82
+    if (!_clname) return 0;
83
+    if (!strcmp(_clname, qt_meta_stringdata_CustomDateRangePopup))
84
+        return static_cast<void*>(const_cast< CustomDateRangePopup*>(this));
85
+    return QWidget::qt_metacast(_clname);
86
+}
87
+
88
+int CustomDateRangePopup::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
89
+{
90
+    _id = QWidget::qt_metacall(_c, _id, _a);
91
+    if (_id < 0)
92
+        return _id;
93
+    if (_c == QMetaObject::InvokeMetaMethod) {
94
+        if (_id < 3)
95
+            qt_static_metacall(this, _c, _id, _a);
96
+        _id -= 3;
97
+    }
98
+    return _id;
99
+}
100
+
101
+// SIGNAL 0
102
+void CustomDateRangePopup::hidingPopup()
103
+{
104
+    QMetaObject::activate(this, &staticMetaObject, 0, 0);
105
+}
106
+static const uint qt_meta_data_DateRangeSelector[] = {
107
+
108
+ // content:
109
+       6,       // revision
110
+       0,       // classname
111
+       0,    0, // classinfo
112
+       8,   14, // methods
113
+       0,    0, // properties
114
+       0,    0, // enums/sets
115
+       0,    0, // constructors
116
+       0,       // flags
117
+       1,       // signalCount
118
+
119
+ // signals: signature, parameters, type, tag, flags
120
+      19,   18,   18,   18, 0x05,
121
+
122
+ // slots: signature, parameters, type, tag, flags
123
+      48,   42,   18,   18, 0x0a,
124
+      87,   69,   18,   18, 0x0a,
125
+     121,   42,   18,   18, 0x0a,
126
+     138,   18,   18,   18, 0x08,
127
+     153,   18,   18,   18, 0x08,
128
+     167,   42,   18,   18, 0x08,
129
+
130
+ // methods: signature, parameters, type, tag, flags
131
+     193,   18,  184,   18, 0x02,
132
+
133
+       0        // eod
134
+};
135
+
136
+static const char qt_meta_stringdata_DateRangeSelector[] = {
137
+    "DateRangeSelector\0\0rangeUpdated(QVariant)\0"
138
+    "index\0setCurrentIndex(int)\0startDate,endDate\0"
139
+    "setLifetimeRange(QString,QString)\0"
140
+    "removeIndex(int)\0toggleCustom()\0"
141
+    "popupHidden()\0updateRange(int)\0QVariant\0"
142
+    "currentRange()\0"
143
+};
144
+
145
+void DateRangeSelector::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
146
+{
147
+    if (_c == QMetaObject::InvokeMetaMethod) {
148
+        Q_ASSERT(staticMetaObject.cast(_o));
149
+        DateRangeSelector *_t = static_cast<DateRangeSelector *>(_o);
150
+        switch (_id) {
151
+        case 0: _t->rangeUpdated((*reinterpret_cast< QVariant(*)>(_a[1]))); break;
152
+        case 1: _t->setCurrentIndex((*reinterpret_cast< int(*)>(_a[1]))); break;
153
+        case 2: _t->setLifetimeRange((*reinterpret_cast< QString(*)>(_a[1])),(*reinterpret_cast< QString(*)>(_a[2]))); break;
154
+        case 3: _t->removeIndex((*reinterpret_cast< int(*)>(_a[1]))); break;
155
+        case 4: _t->toggleCustom(); break;
156
+        case 5: _t->popupHidden(); break;
157
+        case 6: _t->updateRange((*reinterpret_cast< int(*)>(_a[1]))); break;
158
+        case 7: { QVariant _r = _t->currentRange();
159
+            if (_a[0]) *reinterpret_cast< QVariant*>(_a[0]) = _r; }  break;
160
+        default: ;
161
+        }
162
+    }
163
+}
164
+
165
+const QMetaObjectExtraData DateRangeSelector::staticMetaObjectExtraData = {
166
+    0,  qt_static_metacall 
167
+};
168
+
169
+const QMetaObject DateRangeSelector::staticMetaObject = {
170
+    { &QWidget::staticMetaObject, qt_meta_stringdata_DateRangeSelector,
171
+      qt_meta_data_DateRangeSelector, &staticMetaObjectExtraData }
172
+};
173
+
174
+#ifdef Q_NO_DATA_RELOCATION
175
+const QMetaObject &DateRangeSelector::getStaticMetaObject() { return staticMetaObject; }
176
+#endif //Q_NO_DATA_RELOCATION
177
+
178
+const QMetaObject *DateRangeSelector::metaObject() const
179
+{
180
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
181
+}
182
+
183
+void *DateRangeSelector::qt_metacast(const char *_clname)
184
+{
185
+    if (!_clname) return 0;
186
+    if (!strcmp(_clname, qt_meta_stringdata_DateRangeSelector))
187
+        return static_cast<void*>(const_cast< DateRangeSelector*>(this));
188
+    return QWidget::qt_metacast(_clname);
189
+}
190
+
191
+int DateRangeSelector::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
192
+{
193
+    _id = QWidget::qt_metacall(_c, _id, _a);
194
+    if (_id < 0)
195
+        return _id;
196
+    if (_c == QMetaObject::InvokeMetaMethod) {
197
+        if (_id < 8)
198
+            qt_static_metacall(this, _c, _id, _a);
199
+        _id -= 8;
200
+    }
201
+    return _id;
202
+}
203
+
204
+// SIGNAL 0
205
+void DateRangeSelector::rangeUpdated(QVariant _t1)
206
+{
207
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
208
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
209
+}
210
+QT_END_MOC_NAMESPACE

+ 80
- 0
src/moc_draglabel.cpp View File

@@ -0,0 +1,80 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'draglabel.h'
3
+**
4
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6)
5
+**
6
+** WARNING! All changes made in this file will be lost!
7
+*****************************************************************************/
8
+
9
+#include "draglabel.h"
10
+#if !defined(Q_MOC_OUTPUT_REVISION)
11
+#error "The header file 'draglabel.h' doesn't include <QObject>."
12
+#elif Q_MOC_OUTPUT_REVISION != 63
13
+#error "This file was generated using the moc from 4.8.6. It"
14
+#error "cannot be used with the include files from this version of Qt."
15
+#error "(The moc has changed too much.)"
16
+#endif
17
+
18
+QT_BEGIN_MOC_NAMESPACE
19
+static const uint qt_meta_data_DragLabel[] = {
20
+
21
+ // content:
22
+       6,       // revision
23
+       0,       // classname
24
+       0,    0, // classinfo
25
+       0,    0, // methods
26
+       0,    0, // properties
27
+       0,    0, // enums/sets
28
+       0,    0, // constructors
29
+       0,       // flags
30
+       0,       // signalCount
31
+
32
+       0        // eod
33
+};
34
+
35
+static const char qt_meta_stringdata_DragLabel[] = {
36
+    "DragLabel\0"
37
+};
38
+
39
+void DragLabel::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
40
+{
41
+    Q_UNUSED(_o);
42
+    Q_UNUSED(_id);
43
+    Q_UNUSED(_c);
44
+    Q_UNUSED(_a);
45
+}
46
+
47
+const QMetaObjectExtraData DragLabel::staticMetaObjectExtraData = {
48
+    0,  qt_static_metacall 
49
+};
50
+
51
+const QMetaObject DragLabel::staticMetaObject = {
52
+    { &QLabel::staticMetaObject, qt_meta_stringdata_DragLabel,
53
+      qt_meta_data_DragLabel, &staticMetaObjectExtraData }
54
+};
55
+
56
+#ifdef Q_NO_DATA_RELOCATION
57
+const QMetaObject &DragLabel::getStaticMetaObject() { return staticMetaObject; }
58
+#endif //Q_NO_DATA_RELOCATION
59
+
60
+const QMetaObject *DragLabel::metaObject() const
61
+{
62
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
63
+}
64
+
65
+void *DragLabel::qt_metacast(const char *_clname)
66
+{
67
+    if (!_clname) return 0;
68
+    if (!strcmp(_clname, qt_meta_stringdata_DragLabel))
69
+        return static_cast<void*>(const_cast< DragLabel*>(this));
70
+    return QLabel::qt_metacast(_clname);
71
+}
72
+
73
+int DragLabel::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
74
+{
75
+    _id = QLabel::qt_metacall(_c, _id, _a);
76
+    if (_id < 0)
77
+        return _id;
78
+    return _id;
79
+}
80
+QT_END_MOC_NAMESPACE

+ 92
- 0
src/moc_feedback.cpp View File

@@ -0,0 +1,92 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'feedback.h'
3
+**
4
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6)
5
+**
6
+** WARNING! All changes made in this file will be lost!
7
+*****************************************************************************/
8
+
9
+#include "feedback.h"
10
+#if !defined(Q_MOC_OUTPUT_REVISION)
11
+#error "The header file 'feedback.h' doesn't include <QObject>."
12
+#elif Q_MOC_OUTPUT_REVISION != 63
13
+#error "This file was generated using the moc from 4.8.6. It"
14
+#error "cannot be used with the include files from this version of Qt."
15
+#error "(The moc has changed too much.)"
16
+#endif
17
+
18
+QT_BEGIN_MOC_NAMESPACE
19
+static const uint qt_meta_data_FeedbackWizard[] = {
20
+
21
+ // content:
22
+       6,       // revision
23
+       0,       // classname
24
+       0,    0, // classinfo
25
+       1,   14, // methods
26
+       0,    0, // properties
27
+       0,    0, // enums/sets
28
+       0,    0, // constructors
29
+       0,       // flags
30
+       0,       // signalCount
31
+
32
+ // slots: signature, parameters, type, tag, flags
33
+      22,   16,   15,   15, 0x08,
34
+
35
+       0        // eod
36
+};
37
+
38
+static const char qt_meta_stringdata_FeedbackWizard[] = {
39
+    "FeedbackWizard\0\0index\0setCommentInstructions(int)\0"
40
+};
41
+
42
+void FeedbackWizard::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
43
+{
44
+    if (_c == QMetaObject::InvokeMetaMethod) {
45
+        Q_ASSERT(staticMetaObject.cast(_o));
46
+        FeedbackWizard *_t = static_cast<FeedbackWizard *>(_o);
47
+        switch (_id) {
48
+        case 0: _t->setCommentInstructions((*reinterpret_cast< int(*)>(_a[1]))); break;
49
+        default: ;
50
+        }
51
+    }
52
+}
53
+
54
+const QMetaObjectExtraData FeedbackWizard::staticMetaObjectExtraData = {
55
+    0,  qt_static_metacall 
56
+};
57
+
58
+const QMetaObject FeedbackWizard::staticMetaObject = {
59
+    { &QWizard::staticMetaObject, qt_meta_stringdata_FeedbackWizard,
60
+      qt_meta_data_FeedbackWizard, &staticMetaObjectExtraData }
61
+};
62
+
63
+#ifdef Q_NO_DATA_RELOCATION
64
+const QMetaObject &FeedbackWizard::getStaticMetaObject() { return staticMetaObject; }
65
+#endif //Q_NO_DATA_RELOCATION
66
+
67
+const QMetaObject *FeedbackWizard::metaObject() const
68
+{
69
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
70
+}
71
+
72
+void *FeedbackWizard::qt_metacast(const char *_clname)
73
+{
74
+    if (!_clname) return 0;
75
+    if (!strcmp(_clname, qt_meta_stringdata_FeedbackWizard))
76
+        return static_cast<void*>(const_cast< FeedbackWizard*>(this));
77
+    return QWizard::qt_metacast(_clname);
78
+}
79
+
80
+int FeedbackWizard::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
81
+{
82
+    _id = QWizard::qt_metacall(_c, _id, _a);
83
+    if (_id < 0)
84
+        return _id;
85
+    if (_c == QMetaObject::InvokeMetaMethod) {
86
+        if (_id < 1)
87
+            qt_static_metacall(this, _c, _id, _a);
88
+        _id -= 1;
89
+    }
90
+    return _id;
91
+}
92
+QT_END_MOC_NAMESPACE

+ 93
- 0
src/moc_helpmenu.cpp View File

@@ -0,0 +1,93 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'helpmenu.h'
3
+**
4
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6)
5
+**
6
+** WARNING! All changes made in this file will be lost!
7
+*****************************************************************************/
8
+
9
+#include "helpmenu.h"
10
+#if !defined(Q_MOC_OUTPUT_REVISION)
11
+#error "The header file 'helpmenu.h' doesn't include <QObject>."
12
+#elif Q_MOC_OUTPUT_REVISION != 63
13
+#error "This file was generated using the moc from 4.8.6. It"
14
+#error "cannot be used with the include files from this version of Qt."
15
+#error "(The moc has changed too much.)"
16
+#endif
17
+
18
+QT_BEGIN_MOC_NAMESPACE
19
+static const uint qt_meta_data_HelpMenu[] = {
20
+
21
+ // content:
22
+       6,       // revision
23
+       0,       // classname
24
+       0,    0, // classinfo
25
+       1,   14, // methods
26
+       0,    0, // properties
27
+       0,    0, // enums/sets
28
+       0,    0, // constructors
29
+       0,       // flags
30
+       0,       // signalCount
31
+
32
+ // slots: signature, parameters, type, tag, flags
33
+      10,    9,    9,    9, 0x0a,
34
+
35
+       0        // eod
36
+};
37
+
38
+static const char qt_meta_stringdata_HelpMenu[] = {
39
+    "HelpMenu\0\0displayAboutTypica()\0"
40
+};
41
+
42
+void HelpMenu::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
43
+{
44
+    if (_c == QMetaObject::InvokeMetaMethod) {
45
+        Q_ASSERT(staticMetaObject.cast(_o));
46
+        HelpMenu *_t = static_cast<HelpMenu *>(_o);
47
+        switch (_id) {
48
+        case 0: _t->displayAboutTypica(); break;
49
+        default: ;
50
+        }
51
+    }
52
+    Q_UNUSED(_a);
53
+}
54
+
55
+const QMetaObjectExtraData HelpMenu::staticMetaObjectExtraData = {
56
+    0,  qt_static_metacall 
57
+};
58
+
59
+const QMetaObject HelpMenu::staticMetaObject = {
60
+    { &QMenu::staticMetaObject, qt_meta_stringdata_HelpMenu,
61
+      qt_meta_data_HelpMenu, &staticMetaObjectExtraData }
62
+};
63
+
64
+#ifdef Q_NO_DATA_RELOCATION
65
+const QMetaObject &HelpMenu::getStaticMetaObject() { return staticMetaObject; }
66
+#endif //Q_NO_DATA_RELOCATION
67
+
68
+const QMetaObject *HelpMenu::metaObject() const
69
+{
70
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
71
+}
72
+
73
+void *HelpMenu::qt_metacast(const char *_clname)
74
+{
75
+    if (!_clname) return 0;
76
+    if (!strcmp(_clname, qt_meta_stringdata_HelpMenu))
77
+        return static_cast<void*>(const_cast< HelpMenu*>(this));
78
+    return QMenu::qt_metacast(_clname);
79
+}
80
+
81
+int HelpMenu::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
82
+{
83
+    _id = QMenu::qt_metacall(_c, _id, _a);
84
+    if (_id < 0)
85
+        return _id;
86
+    if (_c == QMetaObject::InvokeMetaMethod) {
87
+        if (_id < 1)
88
+            qt_static_metacall(this, _c, _id, _a);
89
+        _id -= 1;
90
+    }
91
+    return _id;
92
+}
93
+QT_END_MOC_NAMESPACE

+ 111
- 0
src/moc_qextserialenumerator.cpp View File

@@ -0,0 +1,111 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'qextserialenumerator.h'
3
+**
4
+** Created: Thu Jul 3 12:13:29 2014
5
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.4)
6
+**
7
+** WARNING! All changes made in this file will be lost!
8
+*****************************************************************************/
9
+
10
+#include "3rdparty/qextserialport/src/qextserialenumerator.h"
11
+#if !defined(Q_MOC_OUTPUT_REVISION)
12
+#error "The header file 'qextserialenumerator.h' doesn't include <QObject>."
13
+#elif Q_MOC_OUTPUT_REVISION != 63
14
+#error "This file was generated using the moc from 4.8.4. It"
15
+#error "cannot be used with the include files from this version of Qt."
16
+#error "(The moc has changed too much.)"
17
+#endif
18
+
19
+QT_BEGIN_MOC_NAMESPACE
20
+static const uint qt_meta_data_QextSerialEnumerator[] = {
21
+
22
+ // content:
23
+       6,       // revision
24
+       0,       // classname
25
+       0,    0, // classinfo
26
+       2,   14, // methods
27
+       0,    0, // properties
28
+       0,    0, // enums/sets
29
+       0,    0, // constructors
30
+       0,       // flags
31
+       2,       // signalCount
32
+
33
+ // signals: signature, parameters, type, tag, flags
34
+      27,   22,   21,   21, 0x05,
35
+      58,   22,   21,   21, 0x05,
36
+
37
+       0        // eod
38
+};
39
+
40
+static const char qt_meta_stringdata_QextSerialEnumerator[] = {
41
+    "QextSerialEnumerator\0\0info\0"
42
+    "deviceDiscovered(QextPortInfo)\0"
43
+    "deviceRemoved(QextPortInfo)\0"
44
+};
45
+
46
+void QextSerialEnumerator::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
47
+{
48
+    if (_c == QMetaObject::InvokeMetaMethod) {
49
+        Q_ASSERT(staticMetaObject.cast(_o));
50
+        QextSerialEnumerator *_t = static_cast<QextSerialEnumerator *>(_o);
51
+        switch (_id) {
52
+        case 0: _t->deviceDiscovered((*reinterpret_cast< const QextPortInfo(*)>(_a[1]))); break;
53
+        case 1: _t->deviceRemoved((*reinterpret_cast< const QextPortInfo(*)>(_a[1]))); break;
54
+        default: ;
55
+        }
56
+    }
57
+}
58
+
59
+const QMetaObjectExtraData QextSerialEnumerator::staticMetaObjectExtraData = {
60
+    0,  qt_static_metacall 
61
+};
62
+
63
+const QMetaObject QextSerialEnumerator::staticMetaObject = {
64
+    { &QObject::staticMetaObject, qt_meta_stringdata_QextSerialEnumerator,
65
+      qt_meta_data_QextSerialEnumerator, &staticMetaObjectExtraData }
66
+};
67
+
68
+#ifdef Q_NO_DATA_RELOCATION
69
+const QMetaObject &QextSerialEnumerator::getStaticMetaObject() { return staticMetaObject; }
70
+#endif //Q_NO_DATA_RELOCATION
71
+
72
+const QMetaObject *QextSerialEnumerator::metaObject() const
73
+{
74
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
75
+}
76
+
77
+void *QextSerialEnumerator::qt_metacast(const char *_clname)
78
+{
79
+    if (!_clname) return 0;
80
+    if (!strcmp(_clname, qt_meta_stringdata_QextSerialEnumerator))
81
+        return static_cast<void*>(const_cast< QextSerialEnumerator*>(this));
82
+    return QObject::qt_metacast(_clname);
83
+}
84
+
85
+int QextSerialEnumerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
86
+{
87
+    _id = QObject::qt_metacall(_c, _id, _a);
88
+    if (_id < 0)
89
+        return _id;
90
+    if (_c == QMetaObject::InvokeMetaMethod) {
91
+        if (_id < 2)
92
+            qt_static_metacall(this, _c, _id, _a);
93
+        _id -= 2;
94
+    }
95
+    return _id;
96
+}
97
+
98
+// SIGNAL 0
99
+void QextSerialEnumerator::deviceDiscovered(const QextPortInfo & _t1)
100
+{
101
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
102
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
103
+}
104
+
105
+// SIGNAL 1
106
+void QextSerialEnumerator::deviceRemoved(const QextPortInfo & _t1)
107
+{
108
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
109
+    QMetaObject::activate(this, &staticMetaObject, 1, _a);
110
+}
111
+QT_END_MOC_NAMESPACE

+ 176
- 0
src/moc_qextserialport.cpp View File

@@ -0,0 +1,176 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'qextserialport.h'
3
+**
4
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6)
5
+**
6
+** WARNING! All changes made in this file will be lost!
7
+*****************************************************************************/
8
+
9
+#include "3rdparty/qextserialport/src/qextserialport.h"
10
+#if !defined(Q_MOC_OUTPUT_REVISION)
11
+#error "The header file 'qextserialport.h' doesn't include <QObject>."
12
+#elif Q_MOC_OUTPUT_REVISION != 63
13
+#error "This file was generated using the moc from 4.8.6. It"
14
+#error "cannot be used with the include files from this version of Qt."
15
+#error "(The moc has changed too much.)"
16
+#endif
17
+
18
+QT_BEGIN_MOC_NAMESPACE
19
+static const uint qt_meta_data_QextSerialPort[] = {
20
+
21
+ // content:
22
+       6,       // revision
23
+       0,       // classname
24
+       0,    0, // classinfo
25
+      14,   14, // methods
26
+       2,   84, // properties
27
+       1,   90, // enums/sets
28
+       0,    0, // constructors
29
+       0,       // flags
30
+       1,       // signalCount
31
+
32
+ // signals: signature, parameters, type, tag, flags
33
+      23,   16,   15,   15, 0x05,
34
+
35
+ // slots: signature, parameters, type, tag, flags
36
+      45,   40,   15,   15, 0x0a,
37
+      71,   66,   15,   15, 0x0a,
38
+      95,   15,   15,   15, 0x0a,
39
+     121,   15,   15,   15, 0x0a,
40
+     147,   15,   15,   15, 0x0a,
41
+     169,   15,   15,   15, 0x0a,
42
+     195,   15,   15,   15, 0x0a,
43
+     220,   15,   15,   15, 0x0a,
44
+     241,  237,   15,   15, 0x0a,
45
+     254,   15,   15,   15, 0x2a,
46
+     263,  237,   15,   15, 0x0a,
47
+     276,   15,   15,   15, 0x2a,
48
+     285,   15,   15,   15, 0x08,
49
+
50
+ // properties: name, type, flags
51
+     306,  298, 0x0a095103,
52
+     325,  315, 0x0009510b,
53
+
54
+ // enums: name, flags, count, data
55
+     315, 0x0,    2,   94,
56
+
57
+ // enum data: key, value
58
+     335, uint(QextSerialPort::Polling),
59
+     343, uint(QextSerialPort::EventDriven),
60
+
61
+       0        // eod
62
+};
63
+
64
+static const char qt_meta_stringdata_QextSerialPort[] = {
65
+    "QextSerialPort\0\0status\0dsrChanged(bool)\0"
66
+    "name\0setPortName(QString)\0mode\0"
67
+    "setQueryMode(QueryMode)\0"
68
+    "setBaudRate(BaudRateType)\0"
69
+    "setDataBits(DataBitsType)\0"
70
+    "setParity(ParityType)\0setStopBits(StopBitsType)\0"
71
+    "setFlowControl(FlowType)\0setTimeout(long)\0"
72
+    "set\0setDtr(bool)\0setDtr()\0setRts(bool)\0"
73
+    "setRts()\0_q_canRead()\0QString\0portName\0"
74
+    "QueryMode\0queryMode\0Polling\0EventDriven\0"
75
+};
76
+
77
+void QextSerialPort::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
78
+{
79
+    if (_c == QMetaObject::InvokeMetaMethod) {
80
+        Q_ASSERT(staticMetaObject.cast(_o));
81
+        QextSerialPort *_t = static_cast<QextSerialPort *>(_o);
82
+        switch (_id) {
83
+        case 0: _t->dsrChanged((*reinterpret_cast< bool(*)>(_a[1]))); break;
84
+        case 1: _t->setPortName((*reinterpret_cast< const QString(*)>(_a[1]))); break;
85
+        case 2: _t->setQueryMode((*reinterpret_cast< QueryMode(*)>(_a[1]))); break;
86
+        case 3: _t->setBaudRate((*reinterpret_cast< BaudRateType(*)>(_a[1]))); break;
87
+        case 4: _t->setDataBits((*reinterpret_cast< DataBitsType(*)>(_a[1]))); break;
88
+        case 5: _t->setParity((*reinterpret_cast< ParityType(*)>(_a[1]))); break;
89
+        case 6: _t->setStopBits((*reinterpret_cast< StopBitsType(*)>(_a[1]))); break;
90
+        case 7: _t->setFlowControl((*reinterpret_cast< FlowType(*)>(_a[1]))); break;
91
+        case 8: _t->setTimeout((*reinterpret_cast< long(*)>(_a[1]))); break;
92
+        case 9: _t->setDtr((*reinterpret_cast< bool(*)>(_a[1]))); break;
93
+        case 10: _t->setDtr(); break;
94
+        case 11: _t->setRts((*reinterpret_cast< bool(*)>(_a[1]))); break;
95
+        case 12: _t->setRts(); break;
96
+        case 13: _t->d_func()->_q_canRead(); break;
97
+        default: ;
98
+        }
99
+    }
100
+}
101
+
102
+const QMetaObjectExtraData QextSerialPort::staticMetaObjectExtraData = {
103
+    0,  qt_static_metacall 
104
+};
105
+
106
+const QMetaObject QextSerialPort::staticMetaObject = {
107
+    { &QIODevice::staticMetaObject, qt_meta_stringdata_QextSerialPort,
108
+      qt_meta_data_QextSerialPort, &staticMetaObjectExtraData }
109
+};
110
+
111
+#ifdef Q_NO_DATA_RELOCATION
112
+const QMetaObject &QextSerialPort::getStaticMetaObject() { return staticMetaObject; }
113
+#endif //Q_NO_DATA_RELOCATION
114
+
115
+const QMetaObject *QextSerialPort::metaObject() const
116
+{
117
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
118
+}
119
+
120
+void *QextSerialPort::qt_metacast(const char *_clname)
121
+{
122
+    if (!_clname) return 0;
123
+    if (!strcmp(_clname, qt_meta_stringdata_QextSerialPort))
124
+        return static_cast<void*>(const_cast< QextSerialPort*>(this));
125
+    return QIODevice::qt_metacast(_clname);
126
+}
127
+
128
+int QextSerialPort::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
129
+{
130
+    _id = QIODevice::qt_metacall(_c, _id, _a);
131
+    if (_id < 0)
132
+        return _id;
133
+    if (_c == QMetaObject::InvokeMetaMethod) {
134
+        if (_id < 14)
135
+            qt_static_metacall(this, _c, _id, _a);
136
+        _id -= 14;
137
+    }
138
+#ifndef QT_NO_PROPERTIES
139
+      else if (_c == QMetaObject::ReadProperty) {
140
+        void *_v = _a[0];
141
+        switch (_id) {
142
+        case 0: *reinterpret_cast< QString*>(_v) = portName(); break;
143
+        case 1: *reinterpret_cast< QueryMode*>(_v) = queryMode(); break;
144
+        }
145
+        _id -= 2;
146
+    } else if (_c == QMetaObject::WriteProperty) {
147
+        void *_v = _a[0];
148
+        switch (_id) {
149
+        case 0: setPortName(*reinterpret_cast< QString*>(_v)); break;
150
+        case 1: setQueryMode(*reinterpret_cast< QueryMode*>(_v)); break;
151
+        }
152
+        _id -= 2;
153
+    } else if (_c == QMetaObject::ResetProperty) {
154
+        _id -= 2;
155
+    } else if (_c == QMetaObject::QueryPropertyDesignable) {
156
+        _id -= 2;
157
+    } else if (_c == QMetaObject::QueryPropertyScriptable) {
158
+        _id -= 2;
159
+    } else if (_c == QMetaObject::QueryPropertyStored) {
160
+        _id -= 2;
161
+    } else if (_c == QMetaObject::QueryPropertyEditable) {
162
+        _id -= 2;
163
+    } else if (_c == QMetaObject::QueryPropertyUser) {
164
+        _id -= 2;
165
+    }
166
+#endif // QT_NO_PROPERTIES
167
+    return _id;
168
+}
169
+
170
+// SIGNAL 0
171
+void QextSerialPort::dsrChanged(bool _t1)
172
+{
173
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
174
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
175
+}
176
+QT_END_MOC_NAMESPACE

+ 109
- 0
src/moc_scale.cpp View File

@@ -0,0 +1,109 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'scale.h'
3
+**
4
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6)
5
+**
6
+** WARNING! All changes made in this file will be lost!
7
+*****************************************************************************/
8
+
9
+#include "scale.h"
10
+#if !defined(Q_MOC_OUTPUT_REVISION)
11
+#error "The header file 'scale.h' doesn't include <QObject>."
12
+#elif Q_MOC_OUTPUT_REVISION != 63
13
+#error "This file was generated using the moc from 4.8.6. It"
14
+#error "cannot be used with the include files from this version of Qt."
15
+#error "(The moc has changed too much.)"
16
+#endif
17
+
18
+QT_BEGIN_MOC_NAMESPACE
19
+static const uint qt_meta_data_SerialScale[] = {
20
+
21
+ // content:
22
+       6,       // revision
23
+       0,       // classname
24
+       0,    0, // classinfo
25
+       4,   14, // methods
26
+       0,    0, // properties
27
+       0,    0, // enums/sets
28
+       0,    0, // constructors
29
+       0,       // flags
30
+       1,       // signalCount
31
+
32
+ // signals: signature, parameters, type, tag, flags
33
+      25,   13,   12,   12, 0x05,
34
+
35
+ // slots: signature, parameters, type, tag, flags
36
+      60,   12,   12,   12, 0x0a,
37
+      67,   12,   12,   12, 0x0a,
38
+      75,   12,   12,   12, 0x08,
39
+
40
+       0        // eod
41
+};
42
+
43
+static const char qt_meta_stringdata_SerialScale[] = {
44
+    "SerialScale\0\0weight,unit\0"
45
+    "newMeasurement(double,Units::Unit)\0"
46
+    "tare()\0weigh()\0dataAvailable()\0"
47
+};
48
+
49
+void SerialScale::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
50
+{
51
+    if (_c == QMetaObject::InvokeMetaMethod) {
52
+        Q_ASSERT(staticMetaObject.cast(_o));
53
+        SerialScale *_t = static_cast<SerialScale *>(_o);
54
+        switch (_id) {
55
+        case 0: _t->newMeasurement((*reinterpret_cast< double(*)>(_a[1])),(*reinterpret_cast< Units::Unit(*)>(_a[2]))); break;
56
+        case 1: _t->tare(); break;
57
+        case 2: _t->weigh(); break;
58
+        case 3: _t->dataAvailable(); break;
59
+        default: ;
60
+        }
61
+    }
62
+}
63
+
64
+const QMetaObjectExtraData SerialScale::staticMetaObjectExtraData = {
65
+    0,  qt_static_metacall 
66
+};
67
+
68
+const QMetaObject SerialScale::staticMetaObject = {
69
+    { &QextSerialPort::staticMetaObject, qt_meta_stringdata_SerialScale,
70
+      qt_meta_data_SerialScale, &staticMetaObjectExtraData }
71
+};
72
+
73
+#ifdef Q_NO_DATA_RELOCATION
74
+const QMetaObject &SerialScale::getStaticMetaObject() { return staticMetaObject; }
75
+#endif //Q_NO_DATA_RELOCATION
76
+
77
+const QMetaObject *SerialScale::metaObject() const
78
+{
79
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
80
+}
81
+
82
+void *SerialScale::qt_metacast(const char *_clname)
83
+{
84
+    if (!_clname) return 0;
85
+    if (!strcmp(_clname, qt_meta_stringdata_SerialScale))
86
+        return static_cast<void*>(const_cast< SerialScale*>(this));
87
+    return QextSerialPort::qt_metacast(_clname);
88
+}
89
+
90
+int SerialScale::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
91
+{
92
+    _id = QextSerialPort::qt_metacall(_c, _id, _a);
93
+    if (_id < 0)
94
+        return _id;
95
+    if (_c == QMetaObject::InvokeMetaMethod) {
96
+        if (_id < 4)
97
+            qt_static_metacall(this, _c, _id, _a);
98
+        _id -= 4;
99
+    }
100
+    return _id;
101
+}
102
+
103
+// SIGNAL 0
104
+void SerialScale::newMeasurement(double _t1, Units::Unit _t2)
105
+{
106
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) };
107
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
108
+}
109
+QT_END_MOC_NAMESPACE

+ 7631
- 0
src/moc_typica.cpp
File diff suppressed because it is too large
View File


+ 96
- 0
src/moc_units.cpp View File

@@ -0,0 +1,96 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'units.h'
3
+**
4
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6)
5
+**
6
+** WARNING! All changes made in this file will be lost!
7
+*****************************************************************************/
8
+
9
+#include "units.h"
10
+#if !defined(Q_MOC_OUTPUT_REVISION)
11
+#error "The header file 'units.h' doesn't include <QObject>."
12
+#elif Q_MOC_OUTPUT_REVISION != 63
13
+#error "This file was generated using the moc from 4.8.6. It"
14
+#error "cannot be used with the include files from this version of Qt."
15
+#error "(The moc has changed too much.)"
16
+#endif
17
+
18
+QT_BEGIN_MOC_NAMESPACE
19
+static const uint qt_meta_data_Units[] = {
20
+
21
+ // content:
22
+       6,       // revision
23
+       0,       // classname
24
+       0,    0, // classinfo
25
+       0,    0, // methods
26
+       0,    0, // properties
27
+       1,   14, // enums/sets
28
+       0,    0, // constructors
29
+       0,       // flags
30
+       0,       // signalCount
31
+
32
+ // enums: name, flags, count, data
33
+       6, 0x0,    9,   18,
34
+
35
+ // enum data: key, value
36
+      11, uint(Units::Unitless),
37
+      20, uint(Units::Fahrenheit),
38
+      31, uint(Units::Celsius),
39
+      39, uint(Units::Kelvin),
40
+      46, uint(Units::Rankine),
41
+      54, uint(Units::Pound),
42
+      60, uint(Units::Kilogram),
43
+      69, uint(Units::Ounce),
44
+      75, uint(Units::Gram),
45
+
46
+       0        // eod
47
+};
48
+
49
+static const char qt_meta_stringdata_Units[] = {
50
+    "Units\0Unit\0Unitless\0Fahrenheit\0Celsius\0"
51
+    "Kelvin\0Rankine\0Pound\0Kilogram\0Ounce\0"
52
+    "Gram\0"
53
+};
54
+
55
+void Units::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
56
+{
57
+    Q_UNUSED(_o);
58
+    Q_UNUSED(_id);
59
+    Q_UNUSED(_c);
60
+    Q_UNUSED(_a);
61
+}
62
+
63
+const QMetaObjectExtraData Units::staticMetaObjectExtraData = {
64
+    0,  qt_static_metacall 
65
+};
66
+
67
+const QMetaObject Units::staticMetaObject = {
68
+    { &QObject::staticMetaObject, qt_meta_stringdata_Units,
69
+      qt_meta_data_Units, &staticMetaObjectExtraData }
70
+};
71
+
72
+#ifdef Q_NO_DATA_RELOCATION
73
+const QMetaObject &Units::getStaticMetaObject() { return staticMetaObject; }
74
+#endif //Q_NO_DATA_RELOCATION
75
+
76
+const QMetaObject *Units::metaObject() const
77
+{
78
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
79
+}
80
+
81
+void *Units::qt_metacast(const char *_clname)
82
+{
83
+    if (!_clname) return 0;
84
+    if (!strcmp(_clname, qt_meta_stringdata_Units))
85
+        return static_cast<void*>(const_cast< Units*>(this));
86
+    return QObject::qt_metacast(_clname);
87
+}
88
+
89
+int Units::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
90
+{
91
+    _id = QObject::qt_metacall(_c, _id, _a);
92
+    if (_id < 0)
93
+        return _id;
94
+    return _id;
95
+}
96
+QT_END_MOC_NAMESPACE

+ 113
- 0
src/moc_webelement.cpp View File

@@ -0,0 +1,113 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'webelement.h'
3
+**
4
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6)
5
+**
6
+** WARNING! All changes made in this file will be lost!
7
+*****************************************************************************/
8
+
9
+#include "webelement.h"
10
+#if !defined(Q_MOC_OUTPUT_REVISION)
11
+#error "The header file 'webelement.h' doesn't include <QObject>."
12
+#elif Q_MOC_OUTPUT_REVISION != 63
13
+#error "This file was generated using the moc from 4.8.6. It"
14
+#error "cannot be used with the include files from this version of Qt."
15
+#error "(The moc has changed too much.)"
16
+#endif
17
+
18
+QT_BEGIN_MOC_NAMESPACE
19
+static const uint qt_meta_data_TypicaWebElement[] = {
20
+
21
+ // content:
22
+       6,       // revision
23
+       0,       // classname
24
+       0,    0, // classinfo
25
+       9,   14, // methods
26
+       0,    0, // properties
27
+       0,    0, // enums/sets
28
+       0,    0, // constructors
29
+       0,       // flags
30
+       0,       // signalCount
31
+
32
+ // methods: signature, parameters, type, tag, flags
33
+      25,   18,   17,   17, 0x02,
34
+      47,   18,   17,   17, 0x02,
35
+      70,   18,   17,   17, 0x02,
36
+      93,   18,   17,   17, 0x02,
37
+     117,   17,   17,   17, 0x02,
38
+     138,   18,   17,   17, 0x02,
39
+     155,   18,   17,   17, 0x02,
40
+     176,   18,   17,   17, 0x02,
41
+     202,  197,   17,   17, 0x02,
42
+
43
+       0        // eod
44
+};
45
+
46
+static const char qt_meta_stringdata_TypicaWebElement[] = {
47
+    "TypicaWebElement\0\0markup\0appendInside(QString)\0"
48
+    "appendOutside(QString)\0prependInside(QString)\0"
49
+    "prependOutside(QString)\0removeFromDocument()\0"
50
+    "replace(QString)\0setInnerXml(QString)\0"
51
+    "setOuterXml(QString)\0text\0"
52
+    "setPlainText(QString)\0"
53
+};
54
+
55
+void TypicaWebElement::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
56
+{
57
+    if (_c == QMetaObject::InvokeMetaMethod) {
58
+        Q_ASSERT(staticMetaObject.cast(_o));
59
+        TypicaWebElement *_t = static_cast<TypicaWebElement *>(_o);
60
+        switch (_id) {
61
+        case 0: _t->appendInside((*reinterpret_cast< const QString(*)>(_a[1]))); break;
62
+        case 1: _t->appendOutside((*reinterpret_cast< const QString(*)>(_a[1]))); break;
63
+        case 2: _t->prependInside((*reinterpret_cast< const QString(*)>(_a[1]))); break;
64
+        case 3: _t->prependOutside((*reinterpret_cast< const QString(*)>(_a[1]))); break;
65
+        case 4: _t->removeFromDocument(); break;
66
+        case 5: _t->replace((*reinterpret_cast< const QString(*)>(_a[1]))); break;
67
+        case 6: _t->setInnerXml((*reinterpret_cast< const QString(*)>(_a[1]))); break;
68
+        case 7: _t->setOuterXml((*reinterpret_cast< const QString(*)>(_a[1]))); break;
69
+        case 8: _t->setPlainText((*reinterpret_cast< const QString(*)>(_a[1]))); break;
70
+        default: ;
71
+        }
72
+    }
73
+}
74
+
75
+const QMetaObjectExtraData TypicaWebElement::staticMetaObjectExtraData = {
76
+    0,  qt_static_metacall 
77
+};
78
+
79
+const QMetaObject TypicaWebElement::staticMetaObject = {
80
+    { &QObject::staticMetaObject, qt_meta_stringdata_TypicaWebElement,
81
+      qt_meta_data_TypicaWebElement, &staticMetaObjectExtraData }
82
+};
83
+
84
+#ifdef Q_NO_DATA_RELOCATION
85
+const QMetaObject &TypicaWebElement::getStaticMetaObject() { return staticMetaObject; }
86
+#endif //Q_NO_DATA_RELOCATION
87
+
88
+const QMetaObject *TypicaWebElement::metaObject() const
89
+{
90
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
91
+}
92
+
93
+void *TypicaWebElement::qt_metacast(const char *_clname)
94
+{
95
+    if (!_clname) return 0;
96
+    if (!strcmp(_clname, qt_meta_stringdata_TypicaWebElement))
97
+        return static_cast<void*>(const_cast< TypicaWebElement*>(this));
98
+    return QObject::qt_metacast(_clname);
99
+}
100
+
101
+int TypicaWebElement::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
102
+{
103
+    _id = QObject::qt_metacall(_c, _id, _a);
104
+    if (_id < 0)
105
+        return _id;
106
+    if (_c == QMetaObject::InvokeMetaMethod) {
107
+        if (_id < 9)
108
+            qt_static_metacall(this, _c, _id, _a);
109
+        _id -= 9;
110
+    }
111
+    return _id;
112
+}
113
+QT_END_MOC_NAMESPACE

+ 130
- 0
src/moc_webview.cpp View File

@@ -0,0 +1,130 @@
1
+/****************************************************************************
2
+** Meta object code from reading C++ file 'webview.h'
3
+**
4
+** Created by: The Qt Meta Object Compiler version 63 (Qt 4.8.6)
5
+**
6
+** WARNING! All changes made in this file will be lost!
7
+*****************************************************************************/
8
+
9
+#include "webview.h"
10
+#if !defined(Q_MOC_OUTPUT_REVISION)
11
+#error "The header file 'webview.h' doesn't include <QObject>."
12
+#elif Q_MOC_OUTPUT_REVISION != 63
13
+#error "This file was generated using the moc from 4.8.6. It"
14
+#error "cannot be used with the include files from this version of Qt."
15
+#error "(The moc has changed too much.)"
16
+#endif
17
+
18
+QT_BEGIN_MOC_NAMESPACE
19
+static const uint qt_meta_data_TypicaWebView[] = {
20
+
21
+ // content:
22
+       6,       // revision
23
+       0,       // classname
24
+       0,    0, // classinfo
25
+      10,   14, // methods
26
+       0,    0, // properties
27
+       0,    0, // enums/sets
28
+       0,    0, // constructors
29
+       0,       // flags
30
+       1,       // signalCount
31
+
32
+ // signals: signature, parameters, type, tag, flags
33
+      20,   15,   14,   14, 0x05,
34
+
35
+ // slots: signature, parameters, type, tag, flags
36
+      51,   47,   14,   14, 0x08,
37
+
38
+ // methods: signature, parameters, type, tag, flags
39
+      70,   47,   14,   14, 0x02,
40
+      84,   14,   14,   14, 0x02,
41
+     105,   92,   14,   14, 0x02,
42
+     132,  127,   14,   14, 0x22,
43
+     156,  149,   14,   14, 0x02,
44
+     187,   14,  179,   14, 0x02,
45
+     209,   14,  197,   14, 0x02,
46
+     236,  227,  197,   14, 0x02,
47
+
48
+       0        // eod
49
+};
50
+
51
+static const char qt_meta_stringdata_TypicaWebView[] = {
52
+    "TypicaWebView\0\0link\0scriptLinkClicked(QString)\0"
53
+    "url\0linkDelegate(QUrl)\0load(QString)\0"
54
+    "print()\0html,baseUrl\0setHtml(QString,QUrl)\0"
55
+    "html\0setHtml(QString)\0device\0"
56
+    "setContent(QIODevice*)\0QString\0saveXml()\0"
57
+    "QWebElement\0documentElement()\0selector\0"
58
+    "findFirstElement(QString)\0"
59
+};
60
+
61
+void TypicaWebView::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
62
+{
63
+    if (_c == QMetaObject::InvokeMetaMethod) {
64
+        Q_ASSERT(staticMetaObject.cast(_o));
65
+        TypicaWebView *_t = static_cast<TypicaWebView *>(_o);
66
+        switch (_id) {
67
+        case 0: _t->scriptLinkClicked((*reinterpret_cast< const QString(*)>(_a[1]))); break;
68
+        case 1: _t->linkDelegate((*reinterpret_cast< const QUrl(*)>(_a[1]))); break;
69
+        case 2: _t->load((*reinterpret_cast< const QString(*)>(_a[1]))); break;
70
+        case 3: _t->print(); break;
71
+        case 4: _t->setHtml((*reinterpret_cast< const QString(*)>(_a[1])),(*reinterpret_cast< const QUrl(*)>(_a[2]))); break;
72
+        case 5: _t->setHtml((*reinterpret_cast< const QString(*)>(_a[1]))); break;
73
+        case 6: _t->setContent((*reinterpret_cast< QIODevice*(*)>(_a[1]))); break;
74
+        case 7: { QString _r = _t->saveXml();
75
+            if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = _r; }  break;
76
+        case 8: { QWebElement _r = _t->documentElement();
77
+            if (_a[0]) *reinterpret_cast< QWebElement*>(_a[0]) = _r; }  break;
78
+        case 9: { QWebElement _r = _t->findFirstElement((*reinterpret_cast< const QString(*)>(_a[1])));
79
+            if (_a[0]) *reinterpret_cast< QWebElement*>(_a[0]) = _r; }  break;
80
+        default: ;
81
+        }
82
+    }
83
+}
84
+
85
+const QMetaObjectExtraData TypicaWebView::staticMetaObjectExtraData = {
86
+    0,  qt_static_metacall 
87
+};
88
+
89
+const QMetaObject TypicaWebView::staticMetaObject = {
90
+    { &QWebView::staticMetaObject, qt_meta_stringdata_TypicaWebView,
91
+      qt_meta_data_TypicaWebView, &staticMetaObjectExtraData }
92
+};
93
+
94
+#ifdef Q_NO_DATA_RELOCATION
95
+const QMetaObject &TypicaWebView::getStaticMetaObject() { return staticMetaObject; }
96
+#endif //Q_NO_DATA_RELOCATION
97
+
98
+const QMetaObject *TypicaWebView::metaObject() const
99
+{
100
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
101
+}
102
+
103
+void *TypicaWebView::qt_metacast(const char *_clname)
104
+{
105
+    if (!_clname) return 0;
106
+    if (!strcmp(_clname, qt_meta_stringdata_TypicaWebView))
107
+        return static_cast<void*>(const_cast< TypicaWebView*>(this));
108
+    return QWebView::qt_metacast(_clname);
109
+}
110
+
111
+int TypicaWebView::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
112
+{
113
+    _id = QWebView::qt_metacall(_c, _id, _a);
114
+    if (_id < 0)
115
+        return _id;
116
+    if (_c == QMetaObject::InvokeMetaMethod) {
117
+        if (_id < 10)
118
+            qt_static_metacall(this, _c, _id, _a);
119
+        _id -= 10;
120
+    }
121
+    return _id;
122
+}
123
+
124
+// SIGNAL 0
125
+void TypicaWebView::scriptLinkClicked(const QString & _t1)
126
+{
127
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
128
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
129
+}
130
+QT_END_MOC_NAMESPACE

+ 491
- 0
src/phidgets.w View File

@@ -0,0 +1,491 @@
1
+@** Phidgets 1048.
2
+
3
+\noindent Phidgets, Inc. has provided one of their four channel temperature
4
+sensor devices so that support could be added to \pn{}. This was originally
5
+planned for version 1.7, however early support was rushed in for the 1.6.3
6
+release. As a result, this support is not full featured, but it should still be
7
+adequate for the most common uses.
8
+
9
+Two configuration widgets are required. The first is for the device as a whole.
10
+
11
+@<Class declarations@>=
12
+class PhidgetsTemperatureSensorConfWidget : public BasicDeviceConfigurationWidget
13
+{
14
+	Q_OBJECT
15
+	public:
16
+		Q_INVOKABLE PhidgetsTemperatureSensorConfWidget(DeviceTreeModel *model,
17
+                                                        const QModelIndex &index);
18
+	private slots:
19
+		void addChannel();
20
+		void updateRate(int ms);
21
+};
22
+
23
+@ This widget allows specification of a device wide sample rate and allows
24
+adding channels for the device to monitor. The device specifications indicate
25
+temperature updates happen up to 25 times per second, but this is generally
26
+excessive for \pn{} so a default rate is set to a multiple of this close to
27
+3 updates per second. There are other options for collecting measurements from
28
+this device and I have not yet had time to experiment with all of the options
29
+to determine the best approach suitable for coffee roasting applications.
30
+
31
+@<Phidgets implementation@>=
32
+PhidgetsTemperatureSensorConfWidget::PhidgetsTemperatureSensorConfWidget(DeviceTreeModel *model,
33
+                                                                         const QModelIndex &index)
34
+	: BasicDeviceConfigurationWidget(model, index)
35
+{
36
+	QFormLayout *layout = new QFormLayout;
37
+	QPushButton *addChannelButton = new QPushButton(tr("Add Channel"));
38
+	QSpinBox *sampleRate = new QSpinBox;
39
+	sampleRate->setMinimum(40);
40
+	sampleRate->setMaximum(600);
41
+	sampleRate->setSingleStep(40);
42
+	sampleRate->setValue(360);
43
+
44
+	@<Get device configuration data for current node@>@;
45
+	for(int i = 0; i < configData.size(); i++)
46
+	{
47
+		node = configData.at(i).toElement();
48
+		if(node.attribute("name") == "sampleRate")
49
+		{
50
+			sampleRate->setValue(node.attribute("value").toInt());
51
+		}
52
+	}
53
+	updateRate(sampleRate->value());
54
+
55
+	connect(sampleRate, SIGNAL(valueChanged(int)), this, SLOT(updateRate(int)));
56
+	connect(addChannelButton, SIGNAL(clicked()), this, SLOT(addChannel()));
57
+
58
+	layout->addRow(addChannelButton);
59
+	layout->addRow(tr("Sample rate:"), sampleRate);
60
+	setLayout(layout);
61
+}
62
+
63
+@ Adding another channel is handled in the usual way, with the channel
64
+configured in a separate widget.
65
+
66
+@<Phidgets implementation@>=
67
+void PhidgetsTemperatureSensorConfWidget::addChannel()
68
+{
69
+	insertChildNode(tr("Channel"), "phidgets1048channel");
70
+}
71
+
72
+@ Changes to the sample rate are saved as an attribute of the node as usual.
73
+
74
+@<Phidgets implementation@>=
75
+void PhidgetsTemperatureSensorConfWidget::updateRate(int ms)
76
+{
77
+	updateAttribute("sampleRate", QString("%1").arg(ms));
78
+}
79
+
80
+@ The other required configuration widget is for a single channel.
81
+
82
+@<Class declarations@>=
83
+class PhidgetTemperatureSensorChannelConfWidget : public BasicDeviceConfigurationWidget
84
+{
85
+	Q_OBJECT
86
+	public:
87
+		Q_INVOKABLE PhidgetTemperatureSensorChannelConfWidget(DeviceTreeModel *model,
88
+                                                              const QModelIndex &index);
89
+	private slots:
90
+		void updateColumnName(const QString &value);
91
+		void updateHidden(bool hidden);
92
+		void updateTC(int index);
93
+		void updateChannel(int channel);
94
+	private:
95
+		QComboBox *tcType;
96
+};
97
+
98
+@ For each channel it is necessary to specify which channel of the device
99
+measurements will come in on. The thermocouple type should be set to match the
100
+type of the thermocouple attached to that channel. The column name and if the
101
+channel is hidden has the same meaning as in channels on other devices.
102
+
103
+@<Phidgets implementation@>=
104
+PhidgetTemperatureSensorChannelConfWidget::PhidgetTemperatureSensorChannelConfWidget(
105
+	DeviceTreeModel *model, const QModelIndex &index)
106
+	: BasicDeviceConfigurationWidget(model, index),
107
+	tcType(new QComboBox)
108
+{
109
+	QFormLayout *layout = new QFormLayout;
110
+	QLineEdit *columnName = new QLineEdit;
111
+	layout->addRow(tr("Column Name:"), columnName);
112
+	QCheckBox *hideSeries = new QCheckBox("Hide this channel");
113
+	layout->addRow(hideSeries);
114
+	layout->addRow(tr("Thermocouple Type:"), tcType);
115
+	tcType->addItem("Type K", "1");
116
+	tcType->addItem("Type J", "2");
117
+	tcType->addItem("Type E", "3");
118
+	tcType->addItem("Type T", "4");
119
+	QSpinBox *channel = new QSpinBox;
120
+	layout->addRow(tr("Channel:"), channel);
121
+	channel->setMinimum(0);
122
+	channel->setMaximum(3);
123
+	setLayout(layout);
124
+	@<Get device configuration data for current node@>@;
125
+	for(int i = 0; i < configData.size(); i++)
126
+	{
127
+		node = configData.at(i).toElement();
128
+		if(node.attribute("name") == "columnname")
129
+		{
130
+			columnName->setText(node.attribute("value"));
131
+		}
132
+		else if(node.attribute("name") == "hidden")
133
+		{
134
+			hideSeries->setChecked(node.attribute("value") == "true");
135
+		}
136
+		else if(node.attribute("name") == "tctype")
137
+		{
138
+			tcType->setCurrentIndex(tcType->findData(node.attribute("value")));
139
+		}
140
+		else if(node.attribute("name") == "channel")
141
+		{
142
+			channel->setValue(node.attribute("value").toInt());
143
+		}
144
+	}
145
+	updateColumnName(columnName->text());
146
+	updateHidden(hideSeries->isChecked());
147
+	updateTC(tcType->currentIndex());
148
+	updateChannel(channel->value());
149
+	connect(columnName, SIGNAL(textEdited(QString)), this, SLOT(updateColumnName(QString)));
150
+	connect(hideSeries, SIGNAL(toggled(bool)), this, SLOT(updateHidden(bool)));
151
+	connect(tcType, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTC(int)));
152
+	connect(channel, SIGNAL(valueChanged(int)), this, SLOT(updateChannel(int)));
153
+}
154
+
155
+@ Channel configuration settings are persisted as they are made.
156
+
157
+@<Phidgets implementation@>=
158
+void PhidgetTemperatureSensorChannelConfWidget::updateColumnName(const QString &value)
159
+{
160
+	updateAttribute("columnname", value);
161
+}
162
+
163
+void PhidgetTemperatureSensorChannelConfWidget::updateHidden(bool hidden)
164
+{
165
+	updateAttribute("hidden", hidden ? "true" : "false");
166
+}
167
+
168
+void PhidgetTemperatureSensorChannelConfWidget::updateTC(int index)
169
+{
170
+	updateAttribute("tctype", tcType->itemData(index).toString());
171
+}
172
+
173
+void PhidgetTemperatureSensorChannelConfWidget::updateChannel(int channel)
174
+{
175
+	updateAttribute("channel", QString("%1").arg(channel));
176
+}
177
+
178
+@ The configuration widgets need to be registered so they can be instantiated as
179
+appropriate.
180
+
181
+@<Register device configuration widgets@>=
182
+app.registerDeviceConfigurationWidget("phidgets1048",
183
+	PhidgetsTemperatureSensorConfWidget::staticMetaObject);
184
+app.registerDeviceConfigurationWidget("phidgets1048channel",
185
+	PhidgetTemperatureSensorChannelConfWidget::staticMetaObject);
186
+
187
+@ A |NodeInserter| for the device node is also required, but this should only
188
+be provided if the required library is installed.
189
+
190
+@<Register top level device configuration nodes@>=
191
+QLibrary phidgetsCheck("phidget21");
192
+if(phidgetsCheck.load())
193
+{
194
+	inserter = new NodeInserter(tr("Phidgets 1048"), tr("Phidgets 1048"),
195
+		"phidgets1048", NULL);
196
+	topLevelNodeInserters.append(inserter);
197
+	phidgetsCheck.unload();
198
+}
199
+else
200
+{
201
+	phidgetsCheck.setFileName("Phidget21.framework/Phidget21");
202
+	if(phidgetsCheck.load())
203
+	{
204
+		inserter = new NodeInserter(tr("Phidgets 1048"), tr("Phidgets 1048"),
205
+		"phidgets1048", NULL);
206
+		topLevelNodeInserters.append(inserter);
207
+		phidgetsCheck.unload();
208
+	}
209
+}
210
+
211
+@ As usual, a class representing the device is provided.
212
+
213
+@<Class declarations@>=
214
+class PhidgetsTemperatureSensor : public QObject
215
+{
216
+	Q_OBJECT
217
+	public:
218
+		Q_INVOKABLE PhidgetsTemperatureSensor(const QModelIndex &deviceIndex);
219
+		Q_INVOKABLE int channelCount();
220
+		Channel* getChannel(int channel);
221
+		Q_INVOKABLE bool isChannelHidden(int channel);
222
+		Q_INVOKABLE QString channelColumnName(int channel);
223
+		Q_INVOKABLE QString channelIndicatorText(int channel);
224
+	public slots:
225
+		void start();
226
+		void stop();
227
+	private slots:
228
+		void getMeasurements();
229
+	private:
230
+		QList<int> channelIndices;
231
+		QList<int> tctypes;
232
+		QList<Channel*> channelList;
233
+		QMap<int, Channel*> channelMap;
234
+		QList<bool> hiddenState;
235
+		QList<QString> columnNames;
236
+		QList<QString> indicatorTexts;
237
+		QLibrary driver;
238
+		QTimer sampleTimer;
239
+		void *device;
240
+		@<Phidgets 1048 function pointers@>@;
241
+};
242
+
243
+@ The constructor uses the configuration data to set up the interface used for
244
+integration with the logging view.
245
+
246
+@<Phidgets implementation@>=
247
+PhidgetsTemperatureSensor::PhidgetsTemperatureSensor(const QModelIndex &index)
248
+	: QObject(NULL), driver("phidget21"), device(NULL)
249
+{
250
+	DeviceTreeModel *model = (DeviceTreeModel *)(index.model());
251
+	QDomElement deviceReferenceElement =
252
+		model->referenceElement(model->data(index, Qt::UserRole).toString());
253
+	QDomNodeList deviceConfigData = deviceReferenceElement.elementsByTagName("attribute");
254
+	QDomElement node;
255
+	for(int i = 0; i < deviceConfigData.size(); i++)
256
+	{
257
+		node = deviceConfigData.at(i).toElement();
258
+		if(node.attribute("name") == "sampleRate")
259
+		{
260
+			sampleTimer.setInterval(node.attribute("value").toInt());
261
+		}
262
+	}
263
+	if(model->hasChildren(index))
264
+	{
265
+		for(int i = 0; i < model->rowCount(index); i++)
266
+		{
267
+			QModelIndex channelIndex = model->index(i, 0, index);
268
+			QDomElement channelReference = model->referenceElement(model->data(channelIndex, 32).toString());
269
+			QDomElement channelReferenceElement = model->referenceElement(model->data(channelIndex, Qt::UserRole).toString());
270
+			QDomNodeList channelConfigData = channelReferenceElement.elementsByTagName("attribute");
271
+			for(int j = 0; j < channelConfigData.size(); j++)
272
+			{
273
+				node = channelConfigData.at(j).toElement();
274
+				if(node.attribute("name") == "channel")
275
+				{
276
+					int channelID = node.attribute("value").toInt();
277
+					channelIndices.append(channelID);
278
+					Channel* channel = new Channel;
279
+					channelList.append(channel);
280
+					channelMap.insert(channelID, channel);
281
+				}
282
+				else if(node.attribute("name") == "hidden")
283
+				{
284
+					hiddenState.append(node.attribute("value") == "true");
285
+				}
286
+				else if(node.attribute("name") == "columnname")
287
+				{
288
+					columnNames.append(node.attribute("value"));
289
+				}
290
+				else if(node.attribute("name") == "tctype")
291
+				{
292
+					tctypes.append(node.attribute("value").toInt());
293
+				}
294
+			}
295
+			indicatorTexts.append(model->data(channelIndex, Qt::DisplayRole).toString());
296
+		}
297
+	}
298
+}
299
+
300
+@ There is a distinction between logical and physical channels. Physical
301
+channels are specified as a configuration attribute and are used for
302
+communication with hardware. Logical channels are determined by the order of
303
+nodes in the configuration and are used for integrating device support with the
304
+rest of the program.
305
+
306
+@<Phidgets implementation@>=
307
+int PhidgetsTemperatureSensor::channelCount()
308
+{
309
+	return channelList.length();
310
+}
311
+
312
+Channel* PhidgetsTemperatureSensor::getChannel(int channel)
313
+{
314
+	return channelList.at(channel);
315
+}
316
+
317
+@ Some information is available about each channel.
318
+
319
+@<Phidgets implementation@>=
320
+bool PhidgetsTemperatureSensor::isChannelHidden(int channel)
321
+{
322
+	return hiddenState.at(channel);
323
+}
324
+
325
+QString PhidgetsTemperatureSensor::channelColumnName(int channel)
326
+{
327
+	if(channel >= 0 && channel < columnNames.length())
328
+	{
329
+		return columnNames.at(channel);
330
+	}
331
+	return QString();
332
+}
333
+
334
+QString PhidgetsTemperatureSensor::channelIndicatorText(int channel)
335
+{
336
+	if(channel >= 0 && channel < indicatorTexts.length())
337
+	{
338
+		return indicatorTexts.at(channel);
339
+	}
340
+	return QString();
341
+}
342
+
343
+@ To avoid introducing dependencies on a library that is only needed for
344
+hardware that may not exist, the phidget21 library is only loaded at runtime
345
+if it is needed. Some function pointers and associated types are, therefore,
346
+required. This approach also means the associated header does not need to
347
+exist at compile time.
348
+
349
+@<Phidgets 1048 function pointers@>=
350
+#ifdef _WIN32
351
+typedef int (__stdcall *PhidgetHandleOnly)(void *);
352
+typedef int (__stdcall *PhidgetHandleInt)(void *, int);
353
+typedef int (__stdcall *PhidgetHandleIntInt)(void *, int, int);
354
+typedef int (__stdcall *PhidgetHandleIntDoubleOut)(void *, int, double*);
355
+#else
356
+typedef int (*PhidgetHandleOnly)(void *);
357
+typedef int (*PhidgetHandleInt)(void *, int);
358
+typedef int (*PhidgetHandleIntInt)(void *, int, int);
359
+typedef int (*PhidgetHandleIntDoubleOut)(void *, int, double*);
360
+#endif
361
+PhidgetHandleOnly createDevice;
362
+PhidgetHandleInt openDevice;
363
+PhidgetHandleInt waitForOpen;
364
+PhidgetHandleIntInt setTCType;
365
+PhidgetHandleIntDoubleOut getTemperature;
366
+PhidgetHandleOnly closeDevice;
367
+PhidgetHandleOnly deleteDevice;
368
+
369
+@ Library loading is deferred until we are ready to open a device.
370
+
371
+@<Phidgets implementation@>=
372
+void PhidgetsTemperatureSensor::start()
373
+{
374
+	if(!driver.load())
375
+	{
376
+		driver.setFileName("Phidget21.framework/Phidget21");
377
+		if(!driver.load())
378
+		{
379
+			QMessageBox::critical(NULL, tr("Typica: Driver not found"),
380
+				tr("Failed to find phidget21. Please install it."));
381
+			return;
382
+		}
383
+	}
384
+	if((createDevice = (PhidgetHandleOnly) driver.resolve("CPhidgetTemperatureSensor_create")) == 0 || @|
385
+       (openDevice = (PhidgetHandleInt) driver.resolve("CPhidget_open")) == 0 || @|
386
+       (waitForOpen = (PhidgetHandleInt) driver.resolve("CPhidget_waitForAttachment")) == 0 || @|
387
+       (setTCType = (PhidgetHandleIntInt) driver.resolve("CPhidgetTemperatureSensor_setThermocoupleType")) == 0 || @|
388
+       (getTemperature = (PhidgetHandleIntDoubleOut) driver.resolve("CPhidgetTemperatureSensor_getTemperature")) == 0 || @|
389
+       (closeDevice = (PhidgetHandleOnly) driver.resolve("CPhidget_close")) == 0 || @|
390
+       (deleteDevice = (PhidgetHandleOnly) driver.resolve("CPhidget_delete")) == 0)
391
+	{
392
+		QMessageBox::critical(NULL, tr("Typica: Link error"),
393
+			tr("Failed to link a required symbol in phidget21."));
394
+		return;
395
+	}
396
+	createDevice(&device);
397
+	openDevice(device, -1);
398
+	int error;
399
+	if(error = waitForOpen(device, 10000))
400
+	{
401
+		closeDevice(device);
402
+		deleteDevice(device);
403
+		QMessageBox::critical(NULL, tr("Typica: Failed to Open Device"),
404
+			tr("CPhidget_waitForAttachment returns error %n", 0, error));
405
+		return;
406
+	}
407
+	for(int i = 0; i < channelIndices.length(); i++)
408
+	{
409
+		setTCType(device, channelIndices.at(i), tctypes.at(i));
410
+	}
411
+	connect(&sampleTimer, SIGNAL(timeout()), this, SLOT(getMeasurements()));
412
+	sampleTimer.start();
413
+}
414
+
415
+@ Once the device is started, we periodically request measurements and pass
416
+them to the appropriate |Channel|.
417
+
418
+@<Phidgets implementation@>=
419
+void PhidgetsTemperatureSensor::getMeasurements()
420
+{
421
+	double value = 0.0;
422
+	QTime time = QTime::currentTime();
423
+	foreach(int i, channelIndices)
424
+	{
425
+		getTemperature(device, i, &value);
426
+		Measurement measure(value * 9.0 / 5.0 + 32.0, time);
427
+		channelMap[i]->input(measure);
428
+	}
429
+}
430
+
431
+@ Some clean up is needed in the |stop()| method.
432
+
433
+@<Phidgets implementation@>=
434
+void PhidgetsTemperatureSensor::stop()
435
+{
436
+	sampleTimer.stop();
437
+	closeDevice(device);
438
+	deleteDevice(device);
439
+	driver.unload();
440
+}
441
+
442
+@ The implementation currently goes into typica.cpp.
443
+
444
+@<Class implementations@>=
445
+@<Phidgets implementation@>@;
446
+
447
+@ The |PhidgetsTemperatureSensor| needs to be available from the host
448
+environment. This detail is likely to change in the future.
449
+
450
+@<Set up the scripting engine@>=
451
+constructor = engine->newFunction(constructPhidgetsTemperatureSensor);
452
+value = engine->newQMetaObject(&PhidgetsTemperatureSensor::staticMetaObject, constructor);
453
+engine->globalObject().setProperty("PhidgetsTemperatureSensor", value);
454
+
455
+@ Two function prototypes are needed.
456
+
457
+@<Function prototypes for scripting@>=
458
+QScriptValue constructPhidgetsTemperatureSensor(QScriptContext *context, QScriptEngine *engine);
459
+QScriptValue Phidgets_getChannel(QScriptContext *context, QScriptEngine *engine);
460
+
461
+@ The script constructor is trivial.
462
+
463
+@<Functions for scripting@>=
464
+QScriptValue constructPhidgetsTemperatureSensor(QScriptContext *context, QScriptEngine *engine)
465
+{
466
+	if(context->argumentCount() != 1)
467
+	{
468
+		context->throwError("Incorrect number of arguments passed to "@|
469
+                            "PhidgetsTemperatureSensor constructor. This takes "@|
470
+                            "a QModelIndex.");
471
+	}
472
+	QScriptValue object = engine->newQObject(new PhidgetsTemperatureSensor(argument<QModelIndex>(0, context)), QScriptEngine::ScriptOwnership);
473
+	setQObjectProperties(object, engine);
474
+	object.setProperty("getChannel", engine->newFunction(Phidgets_getChannel));
475
+	return object;
476
+}
477
+
478
+@ As usual, a wrapper is needed for getting channels.
479
+
480
+@<Functions for scripting@>=
481
+QScriptValue Phidgets_getChannel(QScriptContext *context, QScriptEngine *engine)
482
+{
483
+	PhidgetsTemperatureSensor *self = getself<PhidgetsTemperatureSensor *>(context);
484
+	QScriptValue object;
485
+	if(self)
486
+	{
487
+		object = engine->newQObject(self->getChannel(argument<int>(0, context)));
488
+		setChannelProperties(object, engine);
489
+	}
490
+	return object;
491
+}

+ 31397
- 30656
src/qrc_resources.cpp
File diff suppressed because it is too large
View File


+ 1
- 0
src/resources.qrc View File

@@ -11,6 +11,7 @@
11 11
         <file>resources/icons/tango/scalable/actions/list-remove.svg</file>
12 12
         <file>resources/icons/tango/index.theme</file>
13 13
         <file>resources/icons/appicons/logo.svg</file>
14
+        <file>resources/icons/appicons/logo48.png</file>
14 15
         <file>resources/icons/appicons/logo96.png</file>
15 16
         <file>resources/html/about.html</file>
16 17
         <file>resources/html/style.css</file>

+ 4
- 4
src/resources/Info.plist View File

@@ -7,7 +7,7 @@
7 7
 	<key>CFBundlePackageType</key>
8 8
 	<string>APPL</string>
9 9
 	<key>CFBundleGetInfoString</key>
10
-	<string>Typica 1.6.2</string>
10
+	<string>Typica 1.6.3</string>
11 11
 	<key>CFBundleSignature</key>
12 12
 	<string>@TYPEINFO@</string>
13 13
 	<key>CFBundleExecutable</key>
@@ -17,10 +17,10 @@
17 17
 	<key>CFBundleDisplayName</key>
18 18
 	<string>Typica</string>
19 19
 	<key>CFBundleShortVersionString</key>
20
-	<string>1.6.2</string>
20
+	<string>1.6.3</string>
21 21
 	<key>CFBundleVersion</key>
22
-	<string>1.6.2</string>
22
+	<string>1.6.3</string>
23 23
 	<key>NSHumanReadableCopyright</key>
24
-	<string>© 2007–2014 Neal Wilson</string>
24
+	<string>© 2007–2015 Neal Wilson</string>
25 25
 </dict>
26 26
 </plist>

+ 22
- 5
src/resources/html/about.html View File

@@ -10,10 +10,10 @@
10 10
 				<div id="topbanner">
11 11
 					<a href="http://www.randomfield.com/programs/typica/"><img src="../icons/appicons/logo96.png" height="96px" width="96px" alt="Typica logo" /></a>
12 12
 					<h1><a href="http://www.randomfield.com/programs/typica/">Typica</a></h1>
13
-					<h2>Version 1.6.2</h2>
13
+					<h2>Version 1.6.3</h2>
14 14
 				</div>
15 15
 			<div id="maintext">
16
-				<p>Copyright &copy; 2007&ndash;2014 Neal Evan Wilson
16
+				<p>Copyright &copy; 2007&ndash;2015 Neal Evan Wilson
17 17
 					<span class="icons">
18 18
 						<a href="mailto:roaster@wilsonscoffee.com?subject=Thanks%20for%20Typica&amp;body=Message%20initiated%20from%20Typica.">&#9993;</a>
19 19
 						<a href="https://twitter.com/N3Roaster">&#62217;</a>
@@ -25,9 +25,14 @@
25 25
 				<h3>Special Thanks</h3>
26 26
 				<p>Ongoing development of Typica is made possible through the
27 27
 				generous financial contributions from the following:</p>
28
-
28
+				
29
+				<p>Water Street Coffee Roaster</p>
30
+				<p>La Cosecha Coffee Roasters</p>
29 31
 				<p>Anonymous Funders Through
30
-				<a href="https://www.gittip.com/N3Roaster/">Gittip</a></p>
32
+				<a href="https://www.gratipay.com/N3Roaster/">Gratipay</a></p>
33
+				
34
+				<p>Hardware for developing Phidgets 1048 support was provided
35
+				by Phidgets, Inc.</p>
31 36
 
32 37
 				<h3>Get Involved</h3>
33 38
 				<p>Typica has reached a point in its development where it can
@@ -84,7 +89,7 @@
84 89
 				so please indicate if you would like to be identified by your
85 90
 				name, your company name, or in some other way.</p>
86 91
 				<p>Anonymous funding can also be made through
87
-				<a href="https://www.gittip.com/N3Roaster/">Gittip</a>. While
92
+				<a href="https://www.gratipay.com/N3Roaster/">Gratipay</a>. While
88 93
 				it is not possible to verify and recognize those who help
89 94
 				provide funding in this way, the small weekly payments are
90 95
 				easier to plan around than larger one time contributions.</p>
@@ -142,6 +147,18 @@
142 147
 				<a href="http://tango.freedesktop.org">Tango Desktop Project</a>.</p>
143 148
 				<p>Entypo pictograms by Daniel Bruce &mdash;
144 149
 				<a href="http://www.entypo.com">www.entypo.com</a></p>
150
+				<p>Some charts are produced with the help of d3.js which is provided
151
+				with the following license:</p>
152
+				<p>Copyright &copy; 2010-2014, Michael Bostock</p>
153
+				<p>All rights reserved.</p>
154
+				<p>Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:</p>
155
+				<ul>
156
+					<li>Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
157
+					<li>Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
158
+					<li>The name Michael Bostock may not be used to endorse or promote products derived from this software without specific prior written permission.
159
+				</ul>
160
+				<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>
161
+
145 162
 			</div>
146 163
 		</div>
147 164
 	</body>

+ 6
- 6
src/scale.cpp View File

@@ -1,4 +1,4 @@
1
-/*869:*/
1
+/*978:*/
2 2
 #line 131 "./scales.w"
3 3
 
4 4
 #include "scale.h"
@@ -10,7 +10,7 @@ QextSerialPort(port,QextSerialPort::EventDriven)
10 10
 connect(this,SIGNAL(readyRead()),this,SLOT(dataAvailable()));
11 11
 }
12 12
 
13
-/*:869*//*870:*/
13
+/*:978*//*979:*/
14 14
 #line 149 "./scales.w"
15 15
 
16 16
 void SerialScale::dataAvailable()
@@ -24,7 +24,7 @@ responseBuffer.clear();
24 24
 }
25 25
 else
26 26
 {
27
-/*871:*/
27
+/*980:*/
28 28
 #line 189 "./scales.w"
29 29
 
30 30
 QStringList responseParts= QString(responseBuffer.simplified()).split(' ');
@@ -53,7 +53,7 @@ unit= Units::Ounce;
53 53
 }
54 54
 emit newMeasurement(weight,unit);
55 55
 
56
-/*:871*/
56
+/*:980*/
57 57
 #line 161 "./scales.w"
58 58
 
59 59
 responseBuffer.clear();
@@ -61,7 +61,7 @@ responseBuffer.clear();
61 61
 }
62 62
 }
63 63
 
64
-/*:870*//*872:*/
64
+/*:979*//*981:*/
65 65
 #line 220 "./scales.w"
66 66
 
67 67
 void SerialScale::tare()
@@ -74,4 +74,4 @@ void SerialScale::weigh()
74 74
 write("!KP\x0D");
75 75
 }
76 76
 
77
-/*:872*/
77
+/*:981*/

+ 2
- 2
src/scale.h View File

@@ -1,4 +1,4 @@
1
-/*868:*/
1
+/*977:*/
2 2
 #line 103 "./scales.w"
3 3
 
4 4
 #ifndef TypicaScaleInclude
@@ -25,4 +25,4 @@ QByteArray responseBuffer;
25 25
 
26 26
 #endif
27 27
 
28
-/*:868*/
28
+/*:977*/

+ 5
- 1
src/settings.w View File

@@ -31,6 +31,8 @@ SettingsWindow::SettingsWindow() : QMainWindow(NULL)
31 31
 	settingsTab->addTab(deviceSettings, tr("Roasters"));
32 32
 	GraphSettingsWidget *graphSettings = new GraphSettingsWidget;
33 33
 	settingsTab->addTab(graphSettings, tr("Graph"));
34
+	AdvancedSettingsWidget *advancedSettings = new AdvancedSettingsWidget;
35
+	settingsTab->addTab(advancedSettings, tr("Advanced"));
34 36
 	setCentralWidget(settingsTab);
35 37
 }
36 38
 
@@ -56,4 +58,6 @@ constructor = engine->newFunction(constructSettingsWindow);
56 58
 value = engine->newQMetaObject(&DeviceConfigurationWindow::staticMetaObject, constructor);
57 59
 engine->globalObject().setProperty("SettingsWindow", value);
58 60
 
59
-@i graphsettings.w
61
+@i graphsettings.w
62
+
63
+@i advancedsettings.w

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


+ 6
- 6
src/typica.rc View File

@@ -1,7 +1,7 @@
1 1
 #include <winver.h>
2 2
 VS_VERSION_INFO	VERSIONINFO
3
-FILEVERSION 	1,6,2,0
4
-PRODUCTVERSION 	1,6,2,0
3
+FILEVERSION 	1,6,3,0
4
+PRODUCTVERSION 	1,6,3,0
5 5
 FILEFLAGSMASK	0x3fL
6 6
 #ifdef _DEBUG
7 7
 	FILEFLAGS	VS_FF_DEBUG
@@ -16,13 +16,13 @@ BEGIN
16 16
 		BLOCK "040904b0"
17 17
 		BEGIN
18 18
 			VALUE "CompanyName", "Wilson's Coffee & Tea\0"
19
-			VALUE "FileDescription", "Typica 1.6.2\0"
20
-			VALUE "FileVersion", "1.6.2\0"
19
+			VALUE "FileDescription", "Typica 1.6.3\0"
20
+			VALUE "FileVersion", "1.6.3\0"
21 21
 			VALUE "InternalName", "Typica\0"
22
-			VALUE "LegalCopyright", "Copyright 2007-2014 Neal Evan Wilson\0"
22
+			VALUE "LegalCopyright", "Copyright 2007-2015 Neal Evan Wilson\0"
23 23
 			VALUE "OriginalFilename", "Typica.exe\0"
24 24
 			VALUE "ProductName", "Typica\0"
25
-			VALUE "ProductVersion", "1.6.2\0"
25
+			VALUE "ProductVersion", "1.6.3\0"
26 26
 		END
27 27
 	END
28 28
 	BLOCK "VarFileInfo"

+ 777
- 16
src/typica.w
File diff suppressed because it is too large
View File


+ 6
- 6
src/units.cpp View File

@@ -1,9 +1,9 @@
1
-/*226:*/
1
+/*264:*/
2 2
 #line 42 "./units.w"
3 3
 
4 4
 #include "units.h"
5 5
 
6
-/*:226*//*227:*/
6
+/*:264*//*265:*/
7 7
 #line 52 "./units.w"
8 8
 
9 9
 bool Units::isTemperatureUnit(Unit unit)
@@ -14,7 +14,7 @@ unit==Kelvin||
14 14
 unit==Rankine);
15 15
 }
16 16
 
17
-/*:227*//*228:*/
17
+/*:265*//*266:*/
18 18
 #line 70 "./units.w"
19 19
 
20 20
 double Units::convertTemperature(double value,Unit fromUnit,Unit toUnit)
@@ -100,7 +100,7 @@ break;
100 100
 return 0;
101 101
 }
102 102
 
103
-/*:228*//*229:*/
103
+/*:266*//*267:*/
104 104
 #line 156 "./units.w"
105 105
 
106 106
 double Units::convertRelativeTemperature(double value,Unit fromUnit,Unit toUnit)
@@ -198,7 +198,7 @@ break;
198 198
 return 0;
199 199
 }
200 200
 
201
-/*:229*//*230:*/
201
+/*:267*//*268:*/
202 202
 #line 254 "./units.w"
203 203
 
204 204
 double Units::convertWeight(double value,Unit fromUnit,Unit toUnit)
@@ -303,4 +303,4 @@ unit==Ounce||
303 303
 unit==Gram);
304 304
 }
305 305
 
306
-/*:230*/
306
+/*:268*/

+ 2
- 2
src/units.h View File

@@ -1,4 +1,4 @@
1
-/*225:*/
1
+/*263:*/
2 2
 #line 8 "./units.w"
3 3
 
4 4
 #include <QObject> 
@@ -32,4 +32,4 @@ static bool isWeightUnit(Unit unit);
32 32
 
33 33
 #endif
34 34
 
35
-/*:225*/
35
+/*:263*/

+ 736
- 0
src/unsupportedserial.w View File

@@ -0,0 +1,736 @@
1
+@** Script Driven Devices.
2
+
3
+\noindent There are many data acquisition products that are reasonable to use
4
+with \pn which are not natively supported due to lack of available hardware
5
+for testing, lack of time or money to develop that support, or lack of
6
+documentation. It has also become relatively simple for hardware tinkerers to
7
+develop new devices matching this description as well. Vendors in this space
8
+tend to give inadequate consideration to interoperability and with some devices
9
+the communications protocol used changes significantly between firmware
10
+revisions. There are simply far too many devices like this to support
11
+everything. At the same time there are people with these devices who are
12
+capable of programming the basic communications handling but have difficulty
13
+with integrating that with \pn. By providing an in-program environment that
14
+handles much of the boilerplate and allowing people to write scripts
15
+implementing these protocols without the need to modify core \pn code or
16
+recompile the program, these people may find it easier to make their existing
17
+hardware work. Such scripts can also serve as prototypes for native support.
18
+
19
+Configuration widgets for these devices allow key value pairs to be specified
20
+both at the device level and on a per-channel basis. This is intentionally kept
21
+generic as it is impossible to know what configurable details may be required.
22
+Common configurations will have a device node representing a single logical
23
+device, usually a single physical device but this is not in any way enforced,
24
+and one child node per channel. These details are made available to the device
25
+script and are used to integrate with the logging view.
26
+
27
+Some of the naming conventions used here are legacy of the initial conception
28
+of this feature and should be changed before release if there is time to do so.
29
+While initial support will be focused on devices that present as a serial port,
30
+there is no reason this could not be extended to cover devices that are
31
+interfaced through USB HID, Bluetooth, COM, output piped from an external
32
+console program, devices interfaced through arbitrary libraries, or any other
33
+class of device not directly supported in the core code should there be an
34
+interest in any of these.
35
+
36
+@<Class declarations@>=
37
+class UnsupportedSerialDeviceConfWidget : public BasicDeviceConfigurationWidget
38
+{
39
+	Q_OBJECT
40
+	public:
41
+		Q_INVOKABLE UnsupportedSerialDeviceConfWidget(DeviceTreeModel *model,
42
+		                                              const QModelIndex &index);
43
+	private slots:
44
+		void updateConfiguration();
45
+		void saveScript();
46
+		void addChannel();
47
+	private:
48
+		SaltModel *deviceSettingsModel;
49
+		QTextEdit *scriptEditor;
50
+};
51
+
52
+@ The device configuration widget consists of two tabs. One tab provides a
53
+button for adding channels and an area for entering device specific
54
+configuration details. The other provides an area for entering the device
55
+script. This may be extended later to provide better editing, testing, and
56
+debugging support, but the initial concern is simply having a working feature.
57
+
58
+@<UnsupportedSerialDeviceConfWidget implementation@>=
59
+UnsupportedSerialDeviceConfWidget::UnsupportedSerialDeviceConfWidget(DeviceTreeModel *model,
60
+                                                                     const QModelIndex &index)
61
+	: BasicDeviceConfigurationWidget(model, index),
62
+	deviceSettingsModel(new SaltModel(2)),
63
+	scriptEditor(new QTextEdit)
64
+{
65
+	scriptEditor->setTabStopWidth(20);
66
+	QVBoxLayout *dummyLayout = new QVBoxLayout;
67
+	QTabWidget *central = new QTabWidget;
68
+	QWidget *deviceConfigurationWidget = new QWidget;
69
+	QVBoxLayout *deviceConfigurationLayout = new QVBoxLayout;
70
+	QPushButton *addChannelButton = new QPushButton(tr("Add Channel"));
71
+	deviceConfigurationLayout->addWidget(addChannelButton);
72
+	connect(addChannelButton, SIGNAL(clicked()), this, SLOT(addChannel()));
73
+	QLabel *deviceSettingsLabel = new QLabel(tr("Device Settings:"));
74
+	deviceConfigurationLayout->addWidget(deviceSettingsLabel);
75
+	QTableView *deviceSettingsView = new QTableView;
76
+	deviceSettingsModel->setHeaderData(0, Qt::Horizontal, tr("Key"));
77
+	deviceSettingsModel->setHeaderData(1, Qt::Horizontal, tr("Value"));
78
+	deviceSettingsView->setModel(deviceSettingsModel);
79
+	deviceConfigurationLayout->addWidget(deviceSettingsView);
80
+	
81
+	deviceConfigurationWidget->setLayout(deviceConfigurationLayout);
82
+	central->addTab(deviceConfigurationWidget, tr("Configuration"));
83
+	central->addTab(scriptEditor, tr("Script"));
84
+	dummyLayout->addWidget(central);
85
+	
86
+	@<Get device configuration data for current node@>@;
87
+	for(int i = 0; i < configData.size(); i++)
88
+	{
89
+		node = configData.at(i).toElement();
90
+		if(node.attribute("name") == "keys" || node.attribute("name") == "values")
91
+		{
92
+			int column = 0;
93
+			if(node.attribute("name") == "values")
94
+			{
95
+				column = 1;
96
+			}
97
+			QString data = node.attribute("value");
98
+			if(data.length() > 3)
99
+			{
100
+				data.chop(2);
101
+				data = data.remove(0, 2);
102
+			}
103
+			QStringList keyList = data.split(", ");
104
+			for(int j = 0; j < keyList.size(); j++)
105
+			{
106
+				deviceSettingsModel->setData(deviceSettingsModel->index(j, column),
107
+				                             QVariant(keyList.at(j)),
108
+				                             Qt::EditRole);
109
+			}
110
+		}
111
+		else if(node.attribute("name") == "script")
112
+		{
113
+			scriptEditor->setPlainText(node.attribute("value"));
114
+		}
115
+	}
116
+	
117
+	connect(deviceSettingsModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
118
+	        this, SLOT(updateConfiguration()));
119
+	connect(scriptEditor, SIGNAL(textChanged()), this, SLOT(saveScript()));
120
+	setLayout(dummyLayout);
121
+}
122
+
123
+@ Device configuration data is entered through an ordinary |QTableView| with a
124
+|SaltModel| backing. The original use case for that model does not apply here,
125
+but that model does ensure that additional blank rows are added as needed so
126
+that arbitrarily many key value pairs can be entered. When data changes in the
127
+model we write the full content of the model out. Note that commas may not be
128
+used in keys or values. For keys in which lists make sense, a different
129
+delimiter must be chosen.
130
+
131
+@<UnsupportedSerialDeviceConfWidget implementation@>=
132
+void UnsupportedSerialDeviceConfWidget::updateConfiguration()
133
+{
134
+	updateAttribute("keys", deviceSettingsModel->arrayLiteral(0, Qt::DisplayRole));
135
+	updateAttribute("values", deviceSettingsModel->arrayLiteral(1, Qt::DisplayRole));
136
+}
137
+
138
+@ Every time the script text is changed, the new version of the script is
139
+saved. My expectation is that scripts will either be small or that they will be
140
+pasted in from outside of \pn so that this decision will not cause usability
141
+issues, however if I am wrong about this there may be a need to handle this
142
+more intelligently.
143
+
144
+@<UnsupportedSerialDeviceConfWidget implementation@>=
145
+void UnsupportedSerialDeviceConfWidget::saveScript()
146
+{
147
+	updateAttribute("script", scriptEditor->toPlainText());
148
+}
149
+
150
+@ Typica requires channel nodes to simplify integration with other existing
151
+device code. Providing a new node type allows arbitrary attributes to be
152
+configured on a per-channel basis without resorting to strange conventions in
153
+the device configuration.
154
+
155
+@<UnsupportedSerialDeviceConfWidget implementation@>=
156
+void UnsupportedSerialDeviceConfWidget::addChannel()
157
+{
158
+	insertChildNode(tr("Channel"), "unsupporteddevicechannel");
159
+}
160
+
161
+@ Channel configuration for unsupported devices is like unsupported device
162
+configuration in that arbitrary key value pairs may be entered for use by the
163
+device script. Conventions common to all other channel node types are also
164
+present here.
165
+
166
+@<Class declarations@>=
167
+class UnsupportedDeviceChannelConfWidget : public BasicDeviceConfigurationWidget
168
+{
169
+	Q_OBJECT
170
+	public:
171
+		Q_INVOKABLE UnsupportedDeviceChannelConfWidget(DeviceTreeModel *model,
172
+		                                               const QModelIndex &index);
173
+	private slots:
174
+		void updateColumnName(const QString &value);
175
+		void updateHidden(bool hidden);
176
+		void updateConfiguration();
177
+	private:
178
+		SaltModel *channelSettingsModel;
179
+};
180
+
181
+@ The constructor is typical for for channel node configuraion widgets.
182
+
183
+@<UnsupportedSerialDeviceConfWidget implementation@>=
184
+UnsupportedDeviceChannelConfWidget::UnsupportedDeviceChannelConfWidget(DeviceTreeModel *model,
185
+                                                                       const QModelIndex &index)
186
+	: BasicDeviceConfigurationWidget(model, index),
187
+	channelSettingsModel(new SaltModel(2))
188
+{
189
+	QFormLayout *layout = new QFormLayout;
190
+	QLineEdit *columnName = new QLineEdit;
191
+	layout->addRow(tr("Column Name:"), columnName);
192
+	QCheckBox *hideSeries = new QCheckBox("Hide this channel");
193
+	layout->addRow(hideSeries);
194
+	QTableView *channelSettings = new QTableView;
195
+	channelSettingsModel->setHeaderData(0, Qt::Horizontal, "Key");
196
+	channelSettingsModel->setHeaderData(1, Qt::Horizontal, "Value");
197
+	channelSettings->setModel(channelSettingsModel);
198
+	layout->addRow(channelSettings);
199
+	setLayout(layout);
200
+	@<Get device configuration data for current node@>@;
201
+	for(int i = 0; i < configData.size(); i++)
202
+	{
203
+		node = configData.at(i).toElement();
204
+		if(node.attribute("name") == "columnname")
205
+		{
206
+			columnName->setText(node.attribute("value"));
207
+		}
208
+		else if(node.attribute("name") == "hidden")
209
+		{
210
+			hideSeries->setChecked(node.attribute("value") == "true");
211
+		}
212
+		else if(node.attribute("name") == "keys" || node.attribute("name") == "values")
213
+		{
214
+			int column = 0;
215
+			if(node.attribute("name") == "values")
216
+			{
217
+				column = 1;
218
+			}
219
+			QString data = node.attribute("value");
220
+			if(data.length() > 3)
221
+			{
222
+				data.chop(2);
223
+				data = data.remove(0, 2);
224
+			}
225
+			QStringList keyList = data.split(", ");
226
+			for(int j = 0; j < keyList.size(); j++)
227
+			{
228
+				channelSettingsModel->setData(channelSettingsModel->index(j, column),
229
+				                              QVariant(keyList.at(j)),
230
+				                              Qt::EditRole);
231
+			}
232
+		}
233
+	}
234
+	connect(columnName, SIGNAL(textEdited(QString)), this, SLOT(updateColumnName(QString)));
235
+	connect(hideSeries, SIGNAL(toggled(bool)), this, SLOT(updateHidden(bool)));
236
+	connect(channelSettingsModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
237
+	        this, SLOT(updateConfiguration()));
238
+}
239
+
240
+@ Arbitrary channel configuration data is handled in the same way as device
241
+level settings while the column name and hidden status is handled in the same
242
+way as they are in other channel nodes.
243
+
244
+@<UnsupportedSerialDeviceConfWidget implementation@>=
245
+void UnsupportedDeviceChannelConfWidget::updateColumnName(const QString &value)
246
+{
247
+	updateAttribute("columnname", value);
248
+}
249
+
250
+void UnsupportedDeviceChannelConfWidget::updateHidden(bool hidden)
251
+{
252
+	updateAttribute("hidden", hidden ? "true" : "false");
253
+}
254
+
255
+void UnsupportedDeviceChannelConfWidget::updateConfiguration()
256
+{
257
+	updateAttribute("keys", channelSettingsModel->arrayLiteral(0, Qt::DisplayRole));
258
+	updateAttribute("values", channelSettingsModel->arrayLiteral(1, Qt::DisplayRole));
259
+}
260
+
261
+@ The configuration widgets need to be registered so they can be instantiated
262
+as appropriate.
263
+
264
+@<Register device configuration widgets@>=
265
+app.registerDeviceConfigurationWidget("unsupporteddevicechannel",
266
+	UnsupportedDeviceChannelConfWidget::staticMetaObject);
267
+app.registerDeviceConfigurationWidget("unsupporteddevice",
268
+	UnsupportedSerialDeviceConfWidget::staticMetaObject);
269
+
270
+@ A |NodeInserter| for the device node is also provided.
271
+
272
+@<Register top level device configuration nodes@>=
273
+inserter = new NodeInserter(tr("Other Device"), tr("Other Device"),
274
+	"unsupporteddevice", NULL);
275
+topLevelNodeInserters.append(inserter);
276
+
277
+@ A device abstraction is not strictly required for this feature, however
278
+having one greatly simplifies integrating this feature. At some point I would
279
+like to revise other device abstraction classes so that a huge amount of
280
+boilerplate associated with these can be removed from configuration documents.
281
+
282
+This device abstraction includes features in a few particular categories. First
283
+there are methods that are required for integrating the device with the logging
284
+view. The logging view instantiates the device abstraction, passing in the
285
+configuration data required to properly set up the device. It then is able to
286
+query information about the measurement channels that have been configured for
287
+this device and can set up all of the relevant indicators. Some device classes
288
+may be able to produce annotations, so this class can be treated exactly the
289
+same as any other annotation source. Another requested feature includes the
290
+ability of a device to trigger the start and end of batches, so signals are
291
+provided for this capability. Finally, there are methods associated with
292
+starting and stopping the device. The |start()| method will be called when the
293
+logging view has finished making all of the signal connections. The |stop()|
294
+method will be called when the logging view is closed, giving script code the
295
+chance to cleanly release any resources that must be held for device
296
+communications.
297
+
298
+@<Class declarations@>=
299
+class JavaScriptDevice : public QObject
300
+{
301
+	Q_OBJECT
302
+	public:
303
+		Q_INVOKABLE JavaScriptDevice(const QModelIndex &deviceIndex,
304
+		                             QScriptEngine *engine);
305
+		Q_INVOKABLE int channelCount();
306
+		Channel* getChannel(int channel);
307
+		Q_INVOKABLE bool isChannelHidden(int channel);
308
+		Q_INVOKABLE Units::Unit expectedChannelUnit(int channel);
309
+		Q_INVOKABLE QString channelColumnName(int channel);
310
+		Q_INVOKABLE QString channelIndicatorText(int channel);
311
+	public slots:
312
+		void setTemperatureColumn(int tcol);
313
+		void setAnnotationColumn(int ncol);
314
+		void start();
315
+		void stop();
316
+	signals:
317
+		void annotation(QString note, int tcol, int ncol);
318
+		void triggerStartBatch();
319
+		void triggerStopBatch();
320
+		void deviceStopRequested();
321
+	private:
322
+		QVariantMap deviceSettings;
323
+		QString deviceScript;
324
+		QList<Channel *> channelList;
325
+		QList<bool> hiddenState;
326
+		QList<Units::Unit> channelUnits;
327
+		QList<QString> columnNames;
328
+		QList<QString> indicatorTexts;
329
+		QList<QVariantMap> channelSettings;
330
+		int annotationTemperatureColumn;
331
+		int annotationNoteColumn;
332
+		QScriptEngine *scriptengine;
333
+};
334
+
335
+@ The |JavaScriptDevice| instance provides two interfaces. Its invokable
336
+methods provide the information needed to integrate script driven devices with
337
+a generic logging view. Additional information is also exposed through the host
338
+environment running the device script. This means that the class requires
339
+knowledge of the host environment, which it obtains through a script function
340
+similar to what is done for window creation.
341
+
342
+The name of the function is generic so this may be easily extended later to
343
+create all device abstraction instances.
344
+
345
+@<Function prototypes for scripting@>=
346
+QScriptValue createDevice(QScriptContext *context, QScriptEngine *engine);
347
+
348
+@ That method is made available to the scripting engine.
349
+
350
+@<Set up the scripting engine@>=
351
+engine->globalObject().setProperty("createDevice",
352
+                                   engine->newFunction(createDevice));
353
+
354
+@ This function currently creates a |JavaScriptDevice| from a device
355
+configuration node which must be passed through as an argument.
356
+
357
+@<Functions for scripting@>=
358
+QScriptValue createDevice(QScriptContext *context, QScriptEngine *engine)@/
359
+{
360
+	QModelIndex deviceIndex = argument<QModelIndex>(0, context);
361
+	JavaScriptDevice *device = new JavaScriptDevice(deviceIndex, engine);
362
+	QScriptValue object = engine->newQObject(device);
363
+	setQObjectProperties(object, engine);
364
+	object.setProperty("getChannel", engine->newFunction(JavaScriptDevice_getChannel));
365
+	return object;
366
+}
367
+
368
+@ The |start()| method is responsible for preparing the host environment and
369
+executing the device script.
370
+
371
+@<JavaScriptDevice implementation@>=
372
+void JavaScriptDevice::start()
373
+{
374
+	QScriptValue object = scriptengine->newQObject(this);
375
+	@<Expose device settings as object property@>@;
376
+	@<Expose channels and channel settings to device script@>@;
377
+	QScriptContext *context = scriptengine->currentContext();
378
+	QScriptValue oldThis = context->thisObject();
379
+	context->setThisObject(object);
380
+	QScriptValue result = scriptengine->evaluate(deviceScript);
381
+	QScriptEngine *engine = scriptengine;
382
+	@<Report scripting errors@>@;
383
+	context->setThisObject(oldThis);
384
+}
385
+
386
+@ Device settings are only needed from the device script itself. As such, these
387
+are presented under a settings property available from the |this| object when
388
+the script is run.
389
+
390
+@<Expose device settings as object property@>=
391
+QScriptValue settingsObject = scriptengine->newObject();
392
+QVariantMap::const_iterator i = deviceSettings.constBegin();
393
+while(i != deviceSettings.constEnd())
394
+{
395
+	settingsObject.setProperty(i.key(), i.value().toString());
396
+	i++;
397
+}
398
+object.setProperty("settings", settingsObject);
399
+
400
+@ While channels are available to the device script through the same
401
+|getChannel()| interface used outside of the device script for integration
402
+purposes, it is more convenient to have an array of channels with channel
403
+specific settings as properties of the channel.
404
+
405
+@<Expose channels and channel settings to device script@>=
406
+QScriptValue channelsArray = scriptengine->newArray(channelCount());
407
+for(int i = 0; i < channelCount(); i++)
408
+{
409
+	QScriptValue channelObject = scriptengine->newQObject(getChannel(i));
410
+	QScriptValue channelSettingsObject = scriptengine->newObject();
411
+	QVariantMap::const_iterator j = channelSettings.at(i).constBegin();
412
+	while(j != channelSettings.at(i).constEnd())
413
+	{
414
+		channelSettingsObject.setProperty(j.key(), j.value().toString());
415
+		j++;
416
+	}
417
+	channelObject.setProperty("settings", channelSettingsObject);
418
+	channelsArray.setProperty(i, channelObject);
419
+}
420
+object.setProperty("channels", channelsArray);
421
+
422
+@ Currently we require wrapper functions to work with channels in the host
423
+environment.
424
+
425
+@<Function prototypes for scripting@>=
426
+QScriptValue JavaScriptDevice_getChannel(QScriptContext *context, QScriptEngine *engine);
427
+
428
+@ The implementation is trivial.
429
+
430
+@<Functions for scripting@>=
431
+QScriptValue JavaScriptDevice_getChannel(QScriptContext *context, QScriptEngine *engine)
432
+{
433
+	JavaScriptDevice *self = getself<JavaScriptDevice *>(context);
434
+	QScriptValue object;
435
+	if(self)
436
+	{
437
+		object = engine->newQObject(self->getChannel(argument<int>(0, context)));
438
+		setChannelProperties(object, engine);
439
+	}
440
+	return object;
441
+}
442
+
443
+@ The |stop()| method just fires off a signal that the script can hook into for
444
+any required cleanup.
445
+
446
+@<JavaScriptDevice implementation@>=
447
+void JavaScriptDevice::stop()
448
+{
449
+	emit deviceStopRequested();
450
+}
451
+
452
+@ The constructor is responsible for all boilerplate initialization required
453
+for integrating script defined devices with the logging view.
454
+
455
+Note: At present expected units are assumed to be Fahrenheit. The configuration
456
+widget must be updated to allow at least for control measurements and
457
+eventually support for runtime defined units should also be added.
458
+
459
+@<JavaScriptDevice implementation@>=
460
+JavaScriptDevice::JavaScriptDevice(const QModelIndex &index,
461
+                                   QScriptEngine *engine) :
462
+	QObject(NULL), scriptengine(engine)
463
+{
464
+	DeviceTreeModel *model = (DeviceTreeModel *)(index.model());
465
+	QDomElement deviceReferenceElement =
466
+		model->referenceElement(model->data(index, Qt::UserRole).toString());
467
+	QDomNodeList deviceConfigData = deviceReferenceElement.elementsByTagName("attribute");
468
+	QDomElement node;
469
+	QStringList deviceKeys;
470
+	QStringList deviceValues;
471
+	for(int i = 0; i < deviceConfigData.size(); i++)
472
+	{
473
+		node = deviceConfigData.at(i).toElement();
474
+		if(node.attribute("name") == "keys")
475
+		{
476
+			QString data = node.attribute("value");
477
+			if(data.length() > 3)
478
+			{
479
+				data.chop(2);
480
+				data = data.remove(0, 2);
481
+			}
482
+			deviceKeys = data.split(", ");
483
+		}
484
+		else if(node.attribute("name") == "values")
485
+		{
486
+			QString data = node.attribute("value");
487
+			if(data.length() > 3)
488
+			{
489
+				data.chop(2);
490
+				data = data.remove(0, 2);
491
+			}
492
+			deviceValues = data.split(", ");
493
+		}
494
+		else if(node.attribute("name") == "script")
495
+		{
496
+			deviceScript = node.attribute("value");
497
+		}
498
+		deviceSettings.insert(node.attribute("name"), node.attribute("value"));
499
+	}
500
+	for(int i = 0; i < qMin(deviceKeys.length(), deviceValues.length()); i++)
501
+	{
502
+		deviceSettings.insert(deviceKeys[i], deviceValues[i]);
503
+	}
504
+	if(model->hasChildren(index))
505
+	{
506
+		for(int i = 0; i < model->rowCount(index); i++)
507
+		{
508
+			QModelIndex channelIndex = model->index(i, 0, index);
509
+			QDomElement channelReference = model->referenceElement(model->data(channelIndex, 32).toString());
510
+			channelList.append(new Channel);
511
+			QDomElement channelReferenceElement =
512
+				model->referenceElement(model->data(channelIndex, Qt::UserRole).toString());
513
+			QDomNodeList channelConfigData =
514
+				channelReferenceElement.elementsByTagName("attribute");
515
+			QStringList channelKeys;
516
+			QStringList channelValues;
517
+			for(int j = 0; j < channelConfigData.size(); j++)
518
+			{
519
+				node = channelConfigData.at(j).toElement();
520
+				if(node.attribute("name") == "keys")
521
+				{
522
+					QString data = node.attribute("value");
523
+					if(data.length() > 3)
524
+					{
525
+						data.chop(2);
526
+						data = data.remove(0, 2);
527
+					}
528
+					channelKeys = data.split(", ");
529
+				}
530
+				else if(node.attribute("name") == "values")
531
+				{
532
+					QString data = node.attribute("value");
533
+					if(data.length() > 3)
534
+					{
535
+						data.chop(2);
536
+						data = data.remove(0, 2);
537
+					}
538
+					channelValues = data.split(", ");
539
+				}
540
+				else if(node.attribute("name") == "hidden")
541
+				{
542
+					hiddenState.append(node.attribute("value") == "true");
543
+				}
544
+				else if(node.attribute("name") == "columnname")
545
+				{
546
+					columnNames.append(node.attribute("value"));
547
+				}
548
+			}
549
+			QVariantMap cs;
550
+			for(int j = 0; j < qMin(channelKeys.length(), channelValues.length()); j++)
551
+			{
552
+				cs.insert(channelKeys[j], channelValues[j]);
553
+			}
554
+			channelSettings.append(cs);
555
+			indicatorTexts.append(model->data(channelIndex, Qt::DisplayRole).toString());
556
+			channelUnits.append(Units::Fahrenheit);
557
+		}
558
+	}
559
+}
560
+
561
+@ Several methods are available to query information about the configured
562
+channels.
563
+
564
+@<JavaScriptDevice implementation@>=
565
+int JavaScriptDevice::channelCount()
566
+{
567
+	return channelList.length();
568
+}
569
+
570
+Channel* JavaScriptDevice::getChannel(int channel)
571
+{
572
+	return channelList.at(channel);
573
+}
574
+
575
+bool JavaScriptDevice::isChannelHidden(int channel)
576
+{
577
+	return hiddenState.at(channel);
578
+}
579
+
580
+Units::Unit JavaScriptDevice::expectedChannelUnit(int channel)
581
+{
582
+	return channelUnits.at(channel);
583
+}
584
+
585
+QString JavaScriptDevice::channelColumnName(int channel)
586
+{
587
+	if(channel >= 0 && channel < columnNames.length())
588
+	{
589
+		return columnNames.at(channel);
590
+	}
591
+	return QString();
592
+}
593
+
594
+QString JavaScriptDevice::channelIndicatorText(int channel)
595
+{
596
+	return indicatorTexts.at(channel);
597
+}
598
+
599
+@ Two slots are provided for controlling the placement of annotations.
600
+
601
+@<JavaScriptDevice implementation@>=
602
+void JavaScriptDevice::setTemperatureColumn(int tcol)
603
+{
604
+	annotationTemperatureColumn = tcol;
605
+}
606
+
607
+void JavaScriptDevice::setAnnotationColumn(int ncol)
608
+{
609
+	annotationNoteColumn = ncol;
610
+}
611
+
612
+@ Device scripts must be able to produce measurements on a channel. To do this,
613
+a function is provided for obtaining a timestamp. The returned timestamp should
614
+not be examined as future changes may break assumptions about the content of
615
+the timestamp.
616
+
617
+@<Function prototypes for scripting@>=
618
+QScriptValue getMeasurementTimestamp(QScriptContext *context, QScriptEngine *engine);
619
+
620
+@ That method is made available to the scripting engine.
621
+
622
+@<Set up the scripting engine@>=
623
+engine->globalObject().setProperty("getMeasurementTimestamp",
624
+                                   engine->newFunction(getMeasurementTimestamp));
625
+
626
+@ At present this simply obtains the current system time. It is planned to
627
+switch to a better quality clock in the future, but this should be done for
628
+everything that uses |Measurement| objects at once.
629
+
630
+@<Functions for scripting@>=
631
+QScriptValue getMeasurementTimestamp(QScriptContext *, QScriptEngine *engine)@/
632
+{
633
+	return engine->toScriptValue<QTime>(QTime::currentTime());
634
+}
635
+
636
+@ At present, implementations are not broken out to a separate file. This
637
+should be changed at some point.
638
+
639
+@<Class implementations@>=
640
+@<UnsupportedSerialDeviceConfWidget implementation@>
641
+@<JavaScriptDevice implementation@>
642
+
643
+@* Serial Ports.
644
+
645
+\noindent The first use case for script driven devices was connecting to
646
+devices which present themselves as a serial port. This covers a broad range
647
+of data acquisition products. To provide this support, |QextSerialPort|, which
648
+was already used to support some other hardware options, is directly exposed to
649
+the host environment.
650
+
651
+@<Function prototypes for scripting@>=
652
+QScriptValue constructSerialPort(QScriptContext *context, QScriptEngine *engine);
653
+void setSerialPortProperties(QScriptValue value, QScriptEngine *engine);
654
+QScriptValue SerialPort_flush(QScriptContext *context, QScriptEngine *engine);
655
+
656
+@ Our constructor is passed to the scripting engine.
657
+
658
+@<Set up the scripting engine@>=
659
+constructor = engine->newFunction(constructSerialPort);
660
+value = engine->newQMetaObject(&QextSerialPort::staticMetaObject, constructor);
661
+engine->globalObject().setProperty("SerialPort", value);
662
+
663
+@ At present we only support event driven communications and are not passing
664
+any port settings through the constructor. Such functionality may be added in
665
+the future, but it is not strictly necessary.
666
+
667
+@<Functions for scripting@>=
668
+QScriptValue constructSerialPort(QScriptContext *, QScriptEngine *engine)
669
+{
670
+	QScriptValue object = engine->newQObject(new QextSerialPort());
671
+	setSerialPortProperties(object, engine);
672
+	return object;
673
+}
674
+
675
+@ Some properties of |QIODevice| are brought in as usual for similar subclasses
676
+but we also add a wrapper around the |flush()| method.
677
+
678
+@<Functions for scripting@>=
679
+void setSerialPortProperties(QScriptValue value, QScriptEngine *engine)
680
+{
681
+	setQIODeviceProperties(value, engine);
682
+	value.setProperty("flush", engine->newFunction(SerialPort_flush));
683
+}
684
+
685
+@ The wrapper around |flush()| is trivial.
686
+
687
+@<Functions for scripting@>=
688
+QScriptValue SerialPort_flush(QScriptContext *context, QScriptEngine *)
689
+{
690
+	QextSerialPort *self = getself<QextSerialPort *>(context);
691
+	self->flush();
692
+	return QScriptValue();
693
+}
694
+
695
+@* Timers.
696
+
697
+\noindent While some devices will output a steady stream of measurements which
698
+can be continuously read as they come in, other devices must be polled for
699
+their current state. One approach is to poll the device immediately after
700
+reading the response from the previous polling, but there are times when we may
701
+want to limit the rate at which we poll the device. There are also devices
702
+which specify a length of time during which data should not be sent. For these
703
+cases, we expose |QTimer| to the host environment which allows us to wait. This
704
+is also useful for producing simulations to test features without needing to be
705
+connected to real hardware.
706
+
707
+<@Function prototypes for scripting@>=
708
+void setQTimerProperties(QScriptValue value, QScriptEngine *engine);
709
+QScriptValue constructQTimer(QScriptContext *context, QScriptEngine *engine);
710
+
711
+@ The host environment is informed of the constructor.
712
+
713
+@<Set up the scripting engine@>=
714
+constructor = engine->newFunction(constructQTimer);
715
+value = engine->newQMetaObject(&QTimer::staticMetaObject, constructor);
716
+engine->globalObject().setProperty("Timer", value);
717
+
718
+@ Everything that we are interested in here is a signal, slot, or property so
719
+there is little else to do.
720
+
721
+@<Functions for scripting@>=
722
+void setQTimerProperties(QScriptValue value, QScriptEngine *engine)
723
+{
724
+	setQObjectProperties(value, engine);
725
+}
726
+
727
+QScriptValue constructQTimer(QScriptContext *, QScriptEngine *engine)
728
+{
729
+	QScriptValue object = engine->newQObject(new QTimer);
730
+	setQTimerProperties(object, engine);
731
+	return object;
732
+}
733
+
734
+
735
+
736
+

+ 9
- 9
src/webelement.cpp View File

@@ -1,18 +1,18 @@
1
-/*513:*/
2
-#line 365 "./webview.w"
1
+/*557:*/
2
+#line 368 "./webview.w"
3 3
 
4 4
 #include "webelement.h"
5 5
 
6
-/*511:*/
7
-#line 308 "./webview.w"
6
+/*555:*/
7
+#line 311 "./webview.w"
8 8
 
9 9
 TypicaWebElement::TypicaWebElement(QWebElement element):e(element)
10 10
 {
11 11
 
12 12
 }
13 13
 
14
-/*:511*//*512:*/
15
-#line 317 "./webview.w"
14
+/*:555*//*556:*/
15
+#line 320 "./webview.w"
16 16
 
17 17
 void TypicaWebElement::appendInside(const QString&markup)
18 18
 {
@@ -59,8 +59,8 @@ void TypicaWebElement::setPlainText(const QString&text)
59 59
 e.setPlainText(text);
60 60
 }
61 61
 
62
-/*:512*/
63
-#line 368 "./webview.w"
62
+/*:556*/
63
+#line 371 "./webview.w"
64 64
 
65 65
 
66
-/*:513*/
66
+/*:557*/

+ 3
- 3
src/webelement.h View File

@@ -1,5 +1,5 @@
1
-/*506:*/
2
-#line 245 "./webview.w"
1
+/*550:*/
2
+#line 248 "./webview.w"
3 3
 
4 4
 #include <QWebElement> 
5 5
 #include <QObject> 
@@ -27,4 +27,4 @@ QWebElement e;
27 27
 
28 28
 #endif
29 29
 
30
-/*:506*/
30
+/*:550*/

+ 24
- 22
src/webview.cpp View File

@@ -1,10 +1,10 @@
1
-/*491:*/
2
-#line 49 "./webview.w"
1
+/*535:*/
2
+#line 50 "./webview.w"
3 3
 
4 4
 #include "webview.h"
5 5
 
6
-/*492:*/
7
-#line 56 "./webview.w"
6
+/*536:*/
7
+#line 57 "./webview.w"
8 8
 
9 9
 TypicaWebView::TypicaWebView():QWebView()
10 10
 {
@@ -12,16 +12,16 @@ page()->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
12 12
 connect(page(),SIGNAL(linkClicked(QUrl)),this,SLOT(linkDelegate(QUrl)));
13 13
 }
14 14
 
15
-/*:492*//*493:*/
16
-#line 72 "./webview.w"
15
+/*:536*//*537:*/
16
+#line 73 "./webview.w"
17 17
 
18 18
 void TypicaWebView::linkDelegate(const QUrl&url)
19 19
 {
20 20
 if(url.scheme()=="typica")
21 21
 {
22 22
 QString address(url.toEncoded());
23
-/*494:*/
24
-#line 90 "./webview.w"
23
+/*538:*/
24
+#line 91 "./webview.w"
25 25
 
26 26
 if(address=="typica://aboutqt")
27 27
 {
@@ -29,11 +29,11 @@ QMessageBox::aboutQt(this);
29 29
 return;
30 30
 }
31 31
 
32
-/*:494*/
33
-#line 78 "./webview.w"
32
+/*:538*/
33
+#line 79 "./webview.w"
34 34
 
35
-/*495:*/
36
-#line 99 "./webview.w"
35
+/*539:*/
36
+#line 100 "./webview.w"
37 37
 
38 38
 if(address.startsWith("typica://script/"))
39 39
 {
@@ -41,8 +41,8 @@ emit scriptLinkClicked(address.remove(0,16));
41 41
 return;
42 42
 }
43 43
 
44
-/*:495*/
45
-#line 79 "./webview.w"
44
+/*:539*/
45
+#line 80 "./webview.w"
46 46
 
47 47
 }
48 48
 else
@@ -51,8 +51,8 @@ QDesktopServices::openUrl(url);
51 51
 }
52 52
 }
53 53
 
54
-/*:493*//*496:*/
55
-#line 111 "./webview.w"
54
+/*:537*//*540:*/
55
+#line 112 "./webview.w"
56 56
 
57 57
 void TypicaWebView::load(const QString&url)
58 58
 {
@@ -76,9 +76,11 @@ QWebView::setHtml(html,baseUrl);
76 76
 
77 77
 void TypicaWebView::setContent(QIODevice*device)
78 78
 {
79
+QSettings settings;
79 80
 device->reset();
80 81
 QByteArray content= device->readAll();
81
-QWebView::setContent(content,"application/xhtml+xml");
82
+QUrl baseDir= QUrl("file://"+settings.value("config").toString()+"/");
83
+QWebView::setContent(content,"application/xhtml+xml",baseDir);
82 84
 }
83 85
 
84 86
 QString TypicaWebView::saveXml()
@@ -86,8 +88,8 @@ QString TypicaWebView::saveXml()
86 88
 return page()->currentFrame()->documentElement().toOuterXml();
87 89
 }
88 90
 
89
-/*:496*//*502:*/
90
-#line 202 "./webview.w"
91
+/*:540*//*546:*/
92
+#line 205 "./webview.w"
91 93
 
92 94
 QWebElement TypicaWebView::documentElement()
93 95
 {
@@ -99,8 +101,8 @@ QWebElement TypicaWebView::findFirstElement(const QString&selector)
99 101
 return page()->mainFrame()->findFirstElement(selector);
100 102
 }
101 103
 
102
-/*:502*/
103
-#line 52 "./webview.w"
104
+/*:546*/
105
+#line 53 "./webview.w"
104 106
 
105 107
 
106
-/*:491*/
108
+/*:535*/

+ 3
- 2
src/webview.h View File

@@ -1,4 +1,4 @@
1
-/*490:*/
1
+/*534:*/
2 2
 #line 14 "./webview.w"
3 3
 
4 4
 #include <QWebView> 
@@ -9,6 +9,7 @@
9 9
 #include <QPrintDialog> 
10 10
 #include <QWebFrame> 
11 11
 #include <QWebElement> 
12
+#include <QSettings> 
12 13
 
13 14
 #ifndef TypicaWebViewHeader
14 15
 #define TypicaWebViewHeader
@@ -33,4 +34,4 @@ void linkDelegate(const QUrl&url);
33 34
 
34 35
 #endif
35 36
 
36
-/*:490*/
37
+/*:534*/

+ 4
- 1
src/webview.w View File

@@ -20,6 +20,7 @@ In order to simplify the implementation of certain features, we subclass
20 20
 #include <QPrintDialog>
21 21
 #include <QWebFrame>
22 22
 #include <QWebElement>
23
+#include <QSettings>
23 24
 
24 25
 #ifndef TypicaWebViewHeader
25 26
 #define TypicaWebViewHeader
@@ -131,9 +132,11 @@ void TypicaWebView::setHtml(const QString &html, const QUrl &baseUrl)
131 132
 
132 133
 void TypicaWebView::setContent(QIODevice *device)
133 134
 {
135
+	QSettings settings;
134 136
 	device->reset();
135 137
 	QByteArray content = device->readAll();
136
-	QWebView::setContent(content, "application/xhtml+xml");
138
+	QUrl baseDir = QUrl("file://" + settings.value("config").toString() + "/");
139
+	QWebView::setContent(content, "application/xhtml+xml", baseDir);
137 140
 }
138 141
 
139 142
 QString TypicaWebView::saveXml()

+ 1
- 1
typica.desktop View File

@@ -1,5 +1,5 @@
1 1
 [Desktop Entry]
2
-Version=1.4.3
2
+Version=1.6.3
3 3
 Name=Typica
4 4
 GenericName=Coffee Roasting
5 5
 Comment=Data acquisition, reporting, and management for coffee roasters

Loading…
Cancel
Save