use system palete colors
[makneto-zunavac1.git] / src / ui-mobile / declarative / BoardWidget.qml
blob3c69f10cc33294d5ccfda6d7f46465055b379bcb
1 /*
2  *   Copyright (C) 2011 Lukáš Karas <lukas.karas@centrum.cz>
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program; if not, write to the
16  *   Free Software Foundation, Inc.,
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
20 import QtQuick 1.1 // version 1.1 include PinchArea
21 import Qt 4.7
22 import QtWebKit 1.0
23 import MyTools 1.0 // for WheelArea
25 Rectangle {
26     id: board
27     width: 100
28     height: 62
29     color: "black"
31     signal sendWbMessage(string sessionId, string xmlContent);
32     signal messageSent(string content);
34     property int canvasWidth : 640;
35     property int canvasHeight: 480;
36     //property string uid : "demo@makneto.org"
38     function log(msg){
39         console.log("II [BoardWidget.qml]: "+msg);
40     }
41     function error(msg){
42         console.log("EE [BoardWidget.qml]: "+msg);
43     }
44     function warn(msg){
45         console.log("WW [BoardWidget.qml]: "+msg);
46     }
48     //signal foregroundColorChanged(variant color)
49     signal whiteboardMessageReceived(string data, string contact)
50     property bool whiteboardInitialiyed: false
51     property variant queue: undefined
52     property int animationLength : 200;
54     function onWhiteboardMessageReceived(data, contact){
55         if (whiteboardInitialiyed){
56             whiteboardMessageReceived.disconnect(onWhiteboardMessageReceived);
57             return;
58         }
60         if (queue===undefined)
61             queue = [];
63         var obj = {};
64         obj.contact = contact;
65         obj.data = data;
67         var tmp = queue;
68         tmp.push(obj);
69         queue = tmp;
70         //log("whiteboard is not initialized... "+JSON.stringify(queue.length));
71     }
73     function hideWebView(b){
74         wbView.opacity = b? 0:1;
75         log("HACK: hide wbView? "+b);
76     }
78     state: "normal"
79     states: [
80         State {
81             name: "normal"
82             /*
83             PropertyChanges {
84                 target: stateIcon
85                 source: "img/fullscreen.png"
86             }
87             */
88             AnchorChanges { target: board; anchors.top: board.parent.top; anchors.left: board.parent.left; }
89         },
90         State {
91             name: "fullscreen"
92             /*
93             PropertyChanges {
94                 target: stateIcon
95                 source: "img/minimize.png"
96             }
97             */
98             AnchorChanges { target: board; anchors.right: board.parent.right; anchors.bottom: board.parent.bottom}
99         }
100     ]
103     Behavior on width {
104         NumberAnimation{
105             id: onWidthAnimation
106             duration: animationLength
107         }
108     }
109     Behavior on height {
110         NumberAnimation{
111             id: onHeightAnimation
112             duration: animationLength
113         }
114     }
115     Behavior on opacity {
116         NumberAnimation{duration: animationLength}
117     }
119     transitions: Transition {
120        // smoothly reanchor myRect and move into new position
121        AnchorAnimation { duration: animationLength }
122     }
124     Component.onCompleted: {
125         whiteboardMessageReceived.connect(onWhiteboardMessageReceived);
126         //toolbar.foregroundColorChanged.connect(board.foregroundColorChanged);
127     }
129     Rectangle{
130         id: wrapper
131         color: parent.color
132         anchors{top:parent.top; right: toolbar.left; left: parent.left; bottom: parent.bottom}
134         property int previousWidth : canvasWidth+30
135         property int previousHeith : canvasHeight+30
137         /**
138           * Zooming process:
140                 PinchArea -> resizeAfterPinch -> onSnapshotTaken -> SVG resizing -> canvasResized
142                 For faster and smooth zooming, we use PinchArea with combination WebView scale property.
143                 After touch gesture release, we call resizeAfterPinch(). This take snaphost (graber component)
144                 of current WebView content (scaled) and overlap WebView.
145                 When snapshot is taken and shown (onSnapshotTaken), we invoke SVG scaling inside WebView.
146                 This operation is long (up to seconds) and not smooth. After SVG resizing is caled canvasResized()
147                 method. We currently change WebView size, return scale to 1 and remove old screenshot...
148           */
149         WebView {
150             id: wbView
151             width: canvasWidth * 3
152             height: canvasHeight * 3
153             x: -canvasWidth
154             y: -canvasHeight
155             settings.pluginsEnabled: false
157             property bool whiteboardInitialized : false;
158             property real previousX: 0
159             property real previousY: 0
161             pressGrabTime:0
163             javaScriptWindowObjects: [QtObject {
164                     WebView.windowObjectName: "connector"
165                     function getUID(){ return board.parent.sessionId; }
166                     function init() {
167                         wbView.whiteboardInitialized = true;
168                         log("html part of whiteboard is initialized! lets make big stufs...");
169                         // html part is loaded, init MaknetoWhiteboard...
170                         wbView.evaluateJavaScript ( 'MaknetoWhiteboard.instance.init('+board.canvasWidth+', '+board.canvasHeight+')' );
172                         //var zoom = Math.min( wbView.width / (wbView.contentsSize.width *0.3), wbView.height / (wbView.contentsSize.height *0.3) );
173                         //wbView.evaluateJavaScript ( '$("#workArea").scrollLeft(640)');
174                         //wbView.calculateZoom();
175                         wbView.evaluateJavaScript ( 'MaknetoWhiteboard.instance.setZoom('+ wbView.width / (canvasWidth*3) +')' );
177                         whiteboardMessageReceived.connect(onWhiteboardMessageReceived);
178                         toolbar.foregroundColorChanged.connect(onForegroundColorChanged);
179                         messageSent.connect(onMessageSent);
180                         whiteboardInitialiyed = true;
181                         while (queue !== undefined && queue.length !== 0){
182                             var tmp = queue;
183                             var obj = tmp.shift(); // take first element (from array begin)
184                             queue = tmp;
185                             //log("queue message: "+obj.data);
186                             onWhiteboardMessageReceived(obj.data, obj.contact);
187                         }
188                     }
189                     function onForegroundColorChanged(c){
190                         //log("onForegroundColorChanged "+c);
191                         wbView.evaluateJavaScript ( 'MaknetoWhiteboard.instance.setForegroundColor("'+c+'")' );
192                     }
193                     function onWhiteboardMessageReceived(data, contact){
194                         wbView.evaluateJavaScript ( 'MaknetoWhiteboard.instance.processXmlCommand('+JSON.stringify(data)+')' );
195                         //log("on whiteboard msg received from "+contact+" "+JSON.stringify(data));
196                     }
197                     function onMessageSent(content){
198                         wbView.evaluateJavaScript ( 'MaknetoWhiteboard.instance.messageSent('+JSON.stringify(content)+')' );
199                     }
200                     function sendMessage(xmlMessage){
201                         //log(xmlMessage);
202                         sendWbMessage(board.parent.sessionId, xmlMessage);
203                         return true;
204                     }
205                     function enableRendering(b){
206                         wbView.renderingEnabled = b;
207                     }
208                     function canvasResized(){
209                         var originalWidth = wbView.width;
210                         var originalHeight = wbView.height;
211                         wbView.width = wbView.width * wbView.scale;
212                         wbView.height= wbView.height* wbView.scale;
213                         wbView.scale = 1;
214                         // move to new center
215                         wbView.x = wbView.x + (originalWidth - wbView.width)*.5;
216                         wbView.y = wbView.y + (originalHeight - wbView.height)*.5;
217                         wbView.previousX = wbView.x;
218                         wbView.previousY = wbView.y;
220                         log("resizeAfterPinch "+wbView.scale+" "+Math.floor(originalWidth)+"x"+Math.floor(originalHeight)+
221                             " => "+Math.floor(wbView.width)+"x"+Math.floor(wbView.height)+" ..."+Math.floor(wbView.x) +""+Math.floor(wbView.y)+"");
223                         pinchy.pinch.minimumX = Math.max( (wbView.width  / (-2/3)) , -(wbView.width - wrapper.width));
224                         pinchy.pinch.minimumY = Math.max( (wbView.height / (-2/3)) , -(wbView.height - wrapper.height));
225                         pinchy.pinch.maximumX = 0;
226                         pinchy.pinch.maximumY = 0;
228                         var minWscale = wrapper.width / wbView.width;
229                         var minHscale = wrapper.height / wbView.height;
230                         pinchy.pinch.minimumScale = Math.max(minHscale, minWscale);
231                         var maxWscale = (wrapper.width*15) / wbView.width;
232                         var maxHscale = (wrapper.height*15) / wbView.height;
233                         pinchy.pinch.maximumScale = Math.min(maxHscale, maxWscale);
235                         graber.state = "hide";
236                         //graberTimer.start();
237                     }
238                 },
239                 QtObject {
240                     WebView.windowObjectName: "console"
241                     function log(msg)   { console.log("II [Whiteboard]: " + msg); }
242                     function debug(msg) { console.log("DD [Whiteboard]: " + msg); }
243                     function warn(msg)  { console.log("WW [Whiteboard]: " + msg); }
244                     function error(msg)  { console.log("EE [Whiteboard]: " + msg); }
245                 }
246             ]
248             function resizeAfterPinch(){
250                 // TODO: SHOW waiting cursor while resizing SVG image
251                 graber.takeSnapshot();
252                 graber.state = "visible";
253             }
255             onContentsSizeChanged: {
257             }
259             Component.onCompleted: {
260                 wbView.previousX = wbView.x +15
261                 wbView.previousY = wbView.y +15
262             }
264             //html: "<html><head><script>console.log(\"This is in WebKit!\"); window.connector.qmlCall();</script></head><body><h1>Qt WebKit!</h1></body></html>"
265             url:"qrc:/whiteboard/main.html"
267         }
269         ViewGraber{
270             id: graber
271             opacity: 0
272             anchors.fill: parent
273             delegate: wbView
275             states: [
276                 State {
277                     name: "hide"
278                     PropertyChanges { target: graber; opacity: 0 }
279                     PropertyChanges { target: scalingOverlay; opacity: 0 }
280                 },
281                 State {
282                     name: "visible"
283                     PropertyChanges { target: graber; opacity: 1 }
284                     PropertyChanges { target: scalingOverlay; opacity: 0.2 }
285                 }
286             ]
287             transitions: [
288                 Transition {
289                     from: "hide"; to: "visible"
290                     NumberAnimation { target: graber; properties: "opacity"; duration: 0 }
291                     // when webkit scaling svg image, renderign thread is busy, so we cant draw this nice animation
292                     //NumberAnimation { target: scalingOverlay; properties: "opacity"; duration: 500 }
293                 },
294                 Transition {
295                     from: "visible"; to: "hide"
296                     NumberAnimation { target: graber; properties: "opacity"; duration: 100 }
297                     NumberAnimation { target: scalingOverlay; properties: "opacity"; duration: 5000 } // opacity is multipied by parent's opacity
298                 }
299             ]
300             onSnapshotTaken:{
301                 log("snapshot taken");
302                 wbView.evaluateJavaScript ( 'MaknetoWhiteboard.instance.setZoom('+ wbView.width * wbView.scale / (canvasWidth*3) +')' );
303             }
305             Rectangle{
306                 id: scalingOverlay
307                 opacity: 0
308                 color: "black"
309                 anchors.fill: parent
310             }
311             Text {
312                 id: scalingText
313                 anchors{horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter}
314                 text: "Scaling..."
315             }
316             // discard all mouse operations while scaling
317             PinchArea{ anchors.fill: parent; enabled: true}
318             MouseArea{ anchors.fill: parent }
319             WheelArea{ anchors.fill: parent }
320         }
323         //Rectangle { id: rect; color: "transparent"; border.color: "yellow"; x: 150; y: 150; height: 250; width: 250 }
325         /**
326           * I don't know better solution detect when resize animations stops...
327           */
328         Timer{
329             id: animationNotifier
330             interval: 300; running: false; repeat: false
331             onTriggered: {
332                 wrapper.onDimensionChanged(false);
333             }
334         }
336         // while animation we using QML scale for webView
337         function onDimensionChanged(fast){
338             if (wrapper.width < 10 || wrapper.height < 10)
339                 return;
341             if (fast){
342                 var minWscale = wrapper.width / wbView.width;
343                 var minHscale = wrapper.height / wbView.height;
344                 pinchy.pinch.minimumScale = Math.max(minHscale, minWscale);
346                 var wSc = wrapper.width / previousWidth;
347                 var hSc = wrapper.height / previousHeith;
348                 wbView.scale =  Math.max(pinchy.pinch.minimumScale, Math.min(wSc, hSc)); // FIXME: it is good?
349                 wbView.x = wbView.previousX + (wrapper.width - previousWidth) / 2
350                 wbView.y = wbView.previousY + (wrapper.height - previousHeith) / 2
352                 /*
353                 log("fast scale to "+wbView.scale+"");
354                 log("       "+Math.floor( wbView.previousX)+" + ("+Math.floor(wrapper.width)+" - "+Math.floor(previousWidth)+") / 2) = "+Math.floor( wbView.x)+"");
355                 log("       "+Math.floor( wbView.previousY)+" + ("+Math.floor(wrapper.height)+" - "+Math.floor(previousHeith)+") / 2) = "+Math.floor( wbView.y)+"");
356                 */
358                 animationNotifier.stop();
359                 animationNotifier.start();
360             }else{
361                 wbView.resizeAfterPinch();
362                 previousWidth = wrapper.width;
363                 previousHeith = wrapper.height
364             }
365         }
367         onWidthChanged: {
368             onDimensionChanged(true);
369         }
370         onHeightChanged: {
371             onDimensionChanged(true);
372         }
374         PinchArea {
375             id: pinchy
376             enabled: graber.state == "hide"
377             pinch.target: wbView
378             anchors.fill: parent
379             pinch.dragAxis: Pinch.XandYAxis
380             pinch.minimumScale: 1
381             pinch.maximumScale: 1
382             pinch.minimumX: -canvasWidth*2
383             pinch.maximumX: 0
384             pinch.minimumY: -canvasHeight*2
385             pinch.maximumY: 0
386             //pinch.minimumRotation: -150
387             //pinch.maximumRotation: 150
389             onPinchStarted: {
390                 log("onPinchStarted");
391                 pressDelay.stop();
392                 wbView.evaluateJavaScript("MaknetoWhiteboard.instance.whiteboard.mouseUp2()");
393             }
394             onPinchUpdated: {                
395             }
396             onPinchFinished: {
397                 wbView.resizeAfterPinch();                
398             }
399         }
401         /**
402           * delayed mouse down event is workaround for late detected gestures.
403           */
404         Timer{
405             id: pressDelay
406             property int mouseX;
407             property int mouseY;
408             interval: 200; running: false; repeat: false
409             onTriggered: {
410                 log("delayed mouseDown");
411                 wbView.evaluateJavaScript("MaknetoWhiteboard.instance.whiteboard.mouseDown2("+(mouseX-wbView.x)+", "+(mouseY-wbView.y)+",0)");
412             }
413         }
414         MouseArea {
415             id: mouseArea
416             anchors.fill: parent
417             acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
419             property int startX;
420             property int startY;
421             property int startWbX;
422             property int startWbY;
423             property variant pressed;
425             onPressed:{
426                 //log("mouseArea: onPressed");
427                 //mouse.accepted = false;
428                 startX = mouse.x;
429                 startY = mouse.y;
430                 startWbX = wbView.x;
431                 startWbY = wbView.y;
432                 pressed = mouse.button;
434                 if (mouse.button == Qt.LeftButton){
435                     log("left button");
436                     pressDelay.mouseX = mouse.x;
437                     pressDelay.mouseY = mouse.y;
438                     pressDelay.start();
439                     //wbView.evaluateJavaScript("MaknetoWhiteboard.instance.whiteboard.mouseDown2("+(mouseX-wbView.x)+", "+(mouseY-wbView.y)+",0)");
440                     return;
441                 }
442                 pressDelay.stop();
443             }
444             onDoubleClicked:{
445                 //log("mouseArea: onDoubleClicked");
446             }
447             onPositionChanged:{
448                 //log("mouseArea: onPositionChanged");
449             }
450             onPressAndHold:{
451                 //log("mouseArea: onPressAndHold");                
452             }
453             onReleased :{
454                 //log("mouseArea: onReleased");
455                 pressDelay.stop();
456                 wbView.evaluateJavaScript("MaknetoWhiteboard.instance.whiteboard.mouseUp2()");
457             }
458             onMousePositionChanged: {
459                 //pressDelay.stop();
460                 //log("mouseArea: onMousePositionChanged");
461                 if (pressed == Qt.LeftButton){
462                     //log("      ... paint");
463                     wbView.evaluateJavaScript("MaknetoWhiteboard.instance.whiteboard.mouseMove2("+(mouse.x-wbView.x)+", "+(mouse.y-wbView.y)+")");
464                     return;
465                 }
467                 if (pressed == Qt.RightButton || pressed == Qt.MiddleButton){
468                     pressDelay.stop();
470                     wbView.x = (mouse.x - startX) + startWbX;
471                     wbView.y = (mouse.y - startY) + startWbY;
472                     if (wbView.x < pinchy.pinch.minimumX)
473                         wbView.x = pinchy.pinch.minimumX;
474                     if (wbView.y < pinchy.pinch.minimumY)
475                         wbView.y = pinchy.pinch.minimumY;
476                     if (wbView.x > pinchy.pinch.maximumX)
477                         wbView.x = pinchy.pinch.maximumX;
478                     if (wbView.y > pinchy.pinch.maximumY)
479                         wbView.y = pinchy.pinch.maximumY;
480                     //log("       ... move "+wbView.x+" "+pinchy.pinch.minimumX);
481                     return;
482                 }
483             }
484         }
485         // we use wheel for zooming on desktop...
486         WheelArea {
487             anchors.fill: parent
488             onVerticalWheel: {
489                 //console.log("Vertical Wheel: " + delta)
490                 var scaleRange = pinchy.pinch.maximumScale - pinchy.pinch.minimumScale;
492                 wbView.scale += scaleRange * (delta / 5000);
493                 if (wbView.scale > pinchy.pinch.maximumScale)
494                     wbView.scale = pinchy.pinch.maximumScale;
495                 if (wbView.scale < pinchy.pinch.minimumScale)
496                     wbView.scale = pinchy.pinch.minimumScale;
497                 // FIXME: adjust position when we zoom out
499                 animationNotifier.stop();
500                 animationNotifier.start();
501             }
502             onHorizontalWheel: {
503                 //console.log("Horizontal Wheel: " + delta)
504             }
505         }
506     }
507     BoardToolbar{
508         id: toolbar
509         //color: parent.color
510         anchors{bottom: parent.bottom; right: parent.right; top: parent.top}
511         BorderImage { source: "img/lineedit.sci"; anchors.fill: parent }
512         width: 60
513     }
515     onWidthChanged:{
516         wbView.renderingEnabled = false;
517         wbView.renderingEnabled = true;
518     }
519     onHeightChanged:{
520         wbView.renderingEnabled = false;
521         wbView.renderingEnabled = true;
522     }