1 //===========================================
2 //  Lumina Desktop source code
3 //  Copyright (c) 2017, Ken Moore
4 //  Available under the 3-clause BSD license
5 //  See the LICENSE file for full details
6 //===========================================
7 #include "RootSubWindow.h"
8 #include <QDebug>
9 #include <QApplication>
10 #include <QVBoxLayout>
11 #include <QHBoxLayout>
12 #include <QTimer>
13 #include <QScreen>
14 
15 #define WIN_BORDER 5
16 
17 #include <LIconCache.h>
18 #include <DesktopSettings.h>
19 
20 // === PUBLIC ===
RootSubWindow(QWidget * root,NativeWindow * win)21 RootSubWindow::RootSubWindow(QWidget *root, NativeWindow *win) : QFrame(root){
22   this->setAttribute(Qt::WA_DeleteOnClose);
23   this->setMouseTracking(true);
24   //Create the QWindow and QWidget containers for the window
25   WIN = win;
26   closing = false;
27   initWindowFrame();
28   //Hookup the signals/slots
29   connect(WIN, SIGNAL(PropertiesChanged(QList<NativeWindow::Property>, QList<QVariant>)), this, SLOT(propertiesChanged(QList<NativeWindow::Property>, QList<QVariant>)));
30   WinWidget->embedWindow(WIN);
31   //qDebug() << "[NEW WINDOW]" << WIN->id() << WinWidget->winId() << this->winId();
32   activeState = RootSubWindow::Normal;
33   LoadAllProperties();
34 }
35 
~RootSubWindow()36 RootSubWindow::~RootSubWindow(){
37   //qDebug() << "Visible Window Destroyed";
38   WIN->deleteLater();
39 }
40 
id()41 WId RootSubWindow::id(){
42   return WIN->id();
43 }
44 
nativeWindow()45 NativeWindow* RootSubWindow::nativeWindow(){
46   return WIN;
47 }
48 
49 // === PRIVATE ===
getStateAtPoint(QPoint pt,bool setoffset)50 RootSubWindow::ModState RootSubWindow::getStateAtPoint(QPoint pt, bool setoffset){
51   //Note: pt should be in widget-relative coordinates, not global
52   if(!WinWidget->geometry().contains(pt) && !titleBar->geometry().contains(pt)){
53     //above the frame itself - need to figure out which quadrant it is in (8-directions)
54     if(pt.y() < WIN_BORDER){
55       //One of the top options
56       if(pt.x() < this->width()/5){
57 	if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner
58 	return ResizeTopLeft;
59       }else if(pt.x() > (this->width()*4.0/5.0)){
60 	if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()); } //difference from top-right corner
61 	return ResizeTopRight;
62       }else{
63 	if(setoffset){ offset.setX(0); offset.setY(pt.y()); } //difference from top edge (X does not matter)
64 	return ResizeTop;
65       }
66     }else if(pt.y() > (this->height()-WIN_BORDER) ){
67       //One of the bottom options
68       if(pt.x() < this->width()/5){
69 	if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()-this->height()); } //difference from bottom-left corner
70 	return ResizeBottomLeft;
71       }else if(pt.x() > (this->width()*4.0/5.0)){
72 	if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()-this->height()); } //difference from bottom-right corner
73 	return ResizeBottomRight;
74       }else{
75 	if(setoffset){ offset.setX(0); offset.setY(pt.y()-this->height()); } //difference from bottom edge (X does not matter)
76 	return ResizeBottom;
77       }
78     }else if(pt.x() < WIN_BORDER){
79       //Left side options
80       if(pt.y() < this->height()/5){
81 	if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner
82 	return ResizeTopLeft;
83       }else if(pt.y() > (this->height()*4.0/5.0)){
84 	if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()-this->height()); } //difference from bottom-left corner
85 	return ResizeBottomLeft;
86       }else{
87 	if(setoffset){ offset.setX(pt.x()); offset.setY(0); } //difference from left edge (Y does not matter)
88 	return ResizeLeft;
89       }
90     }else if(pt.x() > (this->width()-WIN_BORDER) ){
91       //Right side options
92       if(pt.y() < this->height()/5){
93 	if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()); } //difference from top-right corner
94 	return ResizeTopRight;
95       }else if(pt.y() > (this->height()*4.0/5.0)){
96 	if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()-this->height()); } //difference from bottom-right corner
97 	return ResizeBottomRight;
98       }else{
99 	if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(0); } //difference from right edge (Y does not matter)
100 	return ResizeRight;
101       }
102     }else{
103 	return Normal;
104     }
105   }
106   //if it gets this far just return normal
107   return Normal;
108 }
109 
setMouseCursor(ModState state,bool override)110 void RootSubWindow::setMouseCursor(ModState state, bool override){
111   if(currentCursor==state && !override){ return; } //nothing to change
112   Qt::CursorShape shape;
113   switch(state){
114     case Normal:
115       shape = Qt::ArrowCursor;
116       break;
117     case Move:
118       shape = Qt::SizeAllCursor;
119       break;
120     case ResizeTop:
121       shape = Qt::SizeVerCursor;
122       break;
123     case ResizeTopRight:
124       shape = Qt::SizeBDiagCursor;
125       break;
126     case ResizeRight:
127       shape = Qt::SizeHorCursor;
128       break;
129     case ResizeBottomRight:
130       shape = Qt::SizeFDiagCursor;
131       break;
132     case ResizeBottom:
133       shape = Qt::SizeVerCursor;
134       break;
135     case ResizeBottomLeft:
136       shape = Qt::SizeBDiagCursor;
137       break;
138     case ResizeLeft:
139       shape = Qt::SizeHorCursor;
140       break;
141     case ResizeTopLeft:
142       shape = Qt::SizeFDiagCursor;
143       break;
144   }
145   if(override){
146     QApplication::setOverrideCursor(QCursor(shape));
147   }else{
148     currentCursor = state;
149     this->setCursor(shape);
150   }
151 }
152 
initWindowFrame()153 void RootSubWindow::initWindowFrame(){
154   //qDebug() << "Create RootSubWindow Frame";
155   this->setContentsMargins(0,0,0,0);
156   mainLayout = new QVBoxLayout(this);
157     mainLayout->setContentsMargins(0,0,0,0);
158   titleBar = new QWidget(this);
159     titleBar->setContentsMargins(0,0,0,0);
160   titleBarL = new QHBoxLayout(titleBar);
161     titleBarL->setContentsMargins(0,0,0,0);
162  closeB = new QToolButton(this);
163   maxB = new QToolButton(this);
164   minB = new QToolButton(this);
165   otherB = new QToolButton(this);
166   anim  = new QPropertyAnimation(this);
167     anim->setTargetObject(this);
168     anim->setDuration(200); //1/5 second (appx)
169   connect(anim, SIGNAL(finished()), this, SLOT(animFinished()) );
170   titleLabel = new QLabel(this);
171     titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
172   otherM = new QMenu(this); //menu of other actions
173     otherB->setMenu(otherM);
174     otherB->setPopupMode(QToolButton::InstantPopup);
175     otherB->setAutoRaise(true);
176   WinWidget = new NativeEmbedWidget(this);
177   connect(closeB, SIGNAL(clicked()), this, SLOT(triggerClose()) );
178   connect(maxB, SIGNAL(clicked()), this, SLOT(toggleMaximize()) );
179   connect(minB, SIGNAL(clicked()), this, SLOT(toggleMinimize()) );
180   //Now assemble the frame layout based on the current settings
181     titleBarL->addWidget(otherB);
182     titleBarL->addWidget(titleLabel);
183     titleBarL->addWidget(minB);
184     titleBarL->addWidget(maxB);
185     titleBarL->addWidget(closeB);
186   WinWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
187   titleBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
188   mainLayout->addWidget(titleBar);
189   mainLayout->addWidget(WinWidget);
190   mainLayout->setAlignment(titleBar, Qt::AlignTop);
191   //Setup the cursors for the buttons
192   closeB->setCursor(Qt::ArrowCursor);
193   minB->setCursor(Qt::ArrowCursor);
194   maxB->setCursor(Qt::ArrowCursor);
195   otherB->setCursor(Qt::ArrowCursor);
196   titleLabel->setCursor(Qt::ArrowCursor);
197   WinWidget->setCursor(Qt::ArrowCursor);
198   //Now all the stylesheet options
199   this->setObjectName("WindowFrame");
200     closeB->setObjectName("Button_Close");
201     minB->setObjectName("Button_Minimize");
202     maxB->setObjectName("Button_Maximize");
203     otherM->setObjectName("Menu_Actions");
204     titleLabel->setObjectName("Label_Title");
205   this->setStyleSheet("QFrame#WindowFrame{background-color: rgba(0,0,0,125)} QWidget#Label_Title{background-color: transparent; color: white; } QToolButton{background-color: transparent; border: 1px solid transparent; border-radius: 3px; } QToolButton::hover{background-color: rgba(255,255,255,150); } QToolButton::pressed{ background-color: white; } QToolButton::menu-arrow{ image: none; }");
206   //And adjust the margins
207   mainLayout->setSpacing(0);
208   titleBarL->setSpacing(1);
209   this->setFrameStyle(QFrame::NoFrame);
210   this->setLineWidth(0);
211   this->setMidLineWidth(0);
212   this->setFrameRect(QRect(0,0,0,0));
213 
214   //Setup the timer object to syncronize info
215   moveTimer = new QTimer(this);
216   moveTimer->setSingleShot(true);
217   moveTimer->setInterval(100); //1/10 second
218   connect(moveTimer, SIGNAL(timeout()), WinWidget, SLOT(resyncWindow()) );
219 
220   //Now load the icons for the button
221   LIconCache::instance()->loadIcon(closeB, "window-close");
222   LIconCache::instance()->loadIcon(maxB, "window-maximize");
223   LIconCache::instance()->loadIcon(minB, "window-minimize");
224   LIconCache::instance()->loadIcon(otherB, "list");
225 }
226 
enableFrame(bool on)227 void RootSubWindow::enableFrame(bool on){
228   //Make the individual frame elements visible as needed
229   if(on){ this->setContentsMargins(WIN_BORDER,WIN_BORDER,WIN_BORDER,WIN_BORDER); }//default border
230   else{ this->setContentsMargins(0, 0, 0, 0); }
231   titleBar->setVisible(on);
232   //And now calculate/save the frame extents
233   QList<int> extents; extents << 0 << 0 << 0 << 0; //left, right, top, bottom
234   if(on){
235     extents[0] = WIN_BORDER;
236     extents[1] = WIN_BORDER;
237     extents[2] = WIN_BORDER + titleBar->height();
238     extents[3] = WIN_BORDER;
239   }
240   //qDebug() << "SET FRAME EXTENTS:" << extents;
241   WIN->requestProperty(NativeWindow::FrameExtents, QVariant::fromValue< QList<int> >(extents) ); //save on raw window itself
242   WIN->setProperty(NativeWindow::FrameExtents, QVariant::fromValue< QList<int> >(extents) ); //save to structure now
243 }
244 
enableFrame(QList<NativeWindow::Type> types)245 void RootSubWindow::enableFrame(QList<NativeWindow::Type> types){
246   static QList<NativeWindow::Type> noframe;
247   if(noframe.isEmpty()){ noframe << NativeWindow::T_DESKTOP << NativeWindow::T_DOCK << NativeWindow::T_TOOLBAR << NativeWindow::T_MENU << NativeWindow::T_SPLASH << NativeWindow::T_DROPDOWN_MENU << NativeWindow::T_POPUP_MENU << NativeWindow::T_TOOLTIP << NativeWindow::T_NOTIFICATION << NativeWindow::T_COMBO << NativeWindow::T_DND; }
248   for(int i=0; i<types.length(); i++){
249     if(noframe.contains(types[i])){ enableFrame(false); return; }
250   }
251   enableFrame(true);
252   //Now make buttons visible as appropriate for the type
253   //NativeWindow::T_UTILITY, NativeWindow::T_DIALOG, , NativeWindow::T_NORMAL
254 }
LoadProperties(QList<NativeWindow::Property> list)255 void RootSubWindow::LoadProperties( QList< NativeWindow::Property> list){
256   QList<QVariant> vals;
257   //Always ensure that visibility changes are evaluated last
258   bool addvisible = false;
259   for(int i=0; i<list.length(); i++){
260     if(list[i] == NativeWindow::Visible){ list.removeAt(i); i--; addvisible = true; continue; }
261     vals << WIN->property(list[i]);
262   }
263   //if(addvisible){ list << NativeWindow::Visible; vals << WIN->property(NativeWindow::Visible); }
264   propertiesChanged(list, vals);
265 }
266 
clientGlobalGeom()267 QRect RootSubWindow::clientGlobalGeom(){
268   QRect tot = this->geometry();
269   QList<int> frame = WIN->property(NativeWindow::FrameExtents).value< QList<int> >();
270   //Now adjust this to take out the frame
271     tot.adjust(frame[0], frame[2], -frame[1], -frame[3]);
272   return tot;
273 }
274 
275 // === PUBLIC SLOTS ===
clientClosed()276 void RootSubWindow::clientClosed(){
277   //qDebug() << "Client Closed";
278   closing = true;
279   if(anim->state()!=QAbstractAnimation::Running){ this->close(); }
280 }
281 
LoadAllProperties()282 void RootSubWindow::LoadAllProperties(){
283   QList< NativeWindow::Property> list;
284   list << NativeWindow::WinTypes << NativeWindow::WinActions << NativeWindow::States
285 	<< NativeWindow::MinSize << NativeWindow::MaxSize << NativeWindow::Title << NativeWindow::ShortTitle
286 	<< NativeWindow::Icon << NativeWindow::Size << NativeWindow::GlobalPos;// << NativeWindow::Visible << NativeWindow::Active;
287   LoadProperties(list);
288   //WIN->requestProperty(NativeWindow::Visible, true);
289 }
290 
291 //Button Actions - public so they can be tied to key shortcuts and stuff as well
toggleMinimize()292 void RootSubWindow::toggleMinimize(){
293   WIN->toggleVisibility();
294 }
295 
toggleMaximize()296 void RootSubWindow::toggleMaximize(){
297   //Get the current screen that this window is on
298   QList<QScreen*> screens = QApplication::screens();
299   QRect rect;
300   int primaryscreen = 0; //fallback value
301   for(int i=0; i<screens.length(); i++){
302     QRect intersect = screens[i]->geometry().intersected(this->geometry());
303     if( (intersect.width()-rect.width() + intersect.height()-rect.height()) > 0){
304       rect = intersect;
305       primaryscreen = i;
306     }
307   }
308   //Now that we have the screen dimensions, lets check/change the window
309   rect = screens[primaryscreen]->availableGeometry();
310   QList< NativeWindow::State > states = WIN->property(NativeWindow::States).value< QList< NativeWindow::State> >();
311   if(rect == this->geometry() || states.contains(NativeWindow::S_MAX_VERT) || states.contains(NativeWindow::S_MAX_HORZ)){
312     //Already maximized - try to restore it to the previous size/location
313     if(!lastMaxGeom.isNull()){
314       rect = lastMaxGeom;
315     }else{
316       // no last geometry - started out maximized?
317       // make it half the screen size and centered on the screen
318       QPoint center = rect.center();
319       rect.setWidth( rect.width()/2 );
320       rect.setHeight( rect.height()/2 );
321       rect.moveTopLeft( center - QPoint(rect.width()/2, rect.height()/2) );
322     }
323     lastMaxGeom = QRect(); //clear this saved geom
324   }else{
325     //Not maximized yet - go ahead and make it so
326     lastMaxGeom = this->geometry(); //save this for later;
327   }
328   //qDebug() << "Toggle Maximize:" << this->geometry() << rect;
329   QString anim_type = DesktopSettings::instance()->value(DesktopSettings::Animation, "window/move", "random").toString();
330   loadAnimation(anim_type, NativeWindow::Size, rect);
331 }
332 
triggerClose()333 void RootSubWindow::triggerClose(){
334   WIN->requestClose();
335 }
336 
toggleSticky()337 void RootSubWindow::toggleSticky(){
338   QList< NativeWindow::State> states = WIN->property(NativeWindow::States).value< QList< NativeWindow::State > >();
339   if(states.contains(NativeWindow::S_STICKY)){
340     states.removeAll(NativeWindow::S_STICKY);
341   }else{
342     states << NativeWindow::S_STICKY;
343   }
344   WIN->requestProperty(NativeWindow::States, QVariant::fromValue<QList <NativeWindow::State> >(states) );
345 }
346 
activate()347 void RootSubWindow::activate(){
348   //WinWidget->raiseWindow();
349   WIN->requestProperty(NativeWindow::Active, true, true);
350 }
351 
352 //Mouse Interactivity
startMoving()353 void RootSubWindow::startMoving(){
354   //If the cursor is not over this window, move it to the center of the titlebar
355   QPoint curpt = QCursor::pos(); //global coords
356   if(!this->geometry().contains(curpt)){
357     curpt = this->mapToGlobal(titleBar->geometry().center());
358     QCursor::setPos(curpt);
359   }
360   //Calculate the offset
361   activeState = Move;
362   offset = this->mapFromGlobal(curpt);
363   setMouseCursor(activeState, true); //this one is an override cursor
364   WinWidget->pause();
365   this->grabMouse();
366 }
367 
startResizing()368 void RootSubWindow::startResizing(){
369   activeState = getStateAtPoint( this->mapFromGlobal(QCursor::pos()), true); //also have it set the offset variable
370   setMouseCursor(activeState, true); //this one is an override cursor
371   WinWidget->pause();
372   this->grabMouse();
373 }
374 
375 // === PRIVATE SLOTS ===
propertiesChanged(QList<NativeWindow::Property> props,QList<QVariant> vals)376 void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList<QVariant> vals){
377   for(int i=0; i<props.length() && i<vals.length(); i++){
378     if(vals[i].isNull()){ continue; } //not the same as a default/empty value - the property has just not been set yet
379     //qDebug() << "RootSubWindow: Property Changed:" << props[i] << vals[i];
380     switch(props[i]){
381 	case NativeWindow::Visible:
382                 if(!WinWidget->isPaused() && (this->isVisible()!=vals[i].toBool()) && activeState==Normal ){
383 		  //qDebug() << "Got Visibility Change:" << vals[i] << this->geometry() << WIN->geometry();
384 		  if(vals[i].toBool()){ loadAnimation( DesktopSettings::instance()->value(DesktopSettings::Animation, "window/appear", "random").toString(), NativeWindow::Visible, vals[i]); }
385 		  else{ loadAnimation( DesktopSettings::instance()->value(DesktopSettings::Animation, "window/disappear", "random").toString(), NativeWindow::Visible, vals[i]); }
386 		}
387 		break;
388 	case NativeWindow::Title:
389 		titleLabel->setText(vals[i].toString());
390 		break;
391 	case NativeWindow::Icon:
392 		//qDebug() << "Got Icon Change:" << vals[i];
393 		if(vals[i].value<QIcon>().isNull() ){ LIconCache::instance()->loadIcon(otherB, "list"); }
394 		else{ otherB->setIcon(vals[i].value<QIcon>()); }
395 		break;
396 	case NativeWindow::GlobalPos:
397 		if(vals[i].toPoint()!=QPoint(0,0)){
398 		  WinWidget->resyncWindow();
399 		}
400 		break;
401 	case NativeWindow::Size:
402 		//qDebug() << " - SIZE CHANGE";
403 		if(WIN->property(NativeWindow::FrameExtents).isNull() && (i<props.indexOf(NativeWindow::FrameExtents)) ){
404 		  //Frame not loaded yet - push this back until after the frame is set
405 		  props << props.takeAt(i);
406 		  vals << vals.takeAt(i);
407 		  i--;
408 		}else if(!WinWidget->isPaused() && activeState==Normal){
409 		  if(WIN->property(NativeWindow::Size).toSize() != WinWidget->size()){
410                     //qDebug() << "Got Direct Geometry Change:" << WIN->geometry();
411 		    this->setGeometry( QRect(this->geometry().topLeft(), WIN->geometry().size()) );
412 		    WinWidget->resyncWindow();
413 		  }
414 		}
415 		break;
416 	case NativeWindow::MinSize:
417 		if(vals[i].toSize().isValid()){
418 		  //Just larger than titlebar, with enough space for 8 characters in the titlebar (+4 buttons)
419 		  //qDebug() << "Got invalid Min Size: Set a reasonable default minimum";
420 		  WinWidget->setMinimumSize( QSize( this->fontMetrics().height()*4 + this->fontMetrics().horizontalAdvance("O")*10, this->fontMetrics().height()*10) );
421 		  WIN->setProperty(NativeWindow::MinSize, WinWidget->minimumSize());
422 		}else{
423 		  WinWidget->setMinimumSize(vals[i].toSize());
424 		}
425 		if(WIN->property(NativeWindow::Size).toSize().width() < WinWidget->minimumSize().width() \
426 		     || WIN->property(NativeWindow::Size).toSize().height() < WinWidget->minimumSize().height()  ){
427 		  WIN->setProperty(NativeWindow::Size, WinWidget->minimumSize(), true); //force this
428 		  //WinWidget->resize(WinWidget->minimumSize());
429 		}
430 		break;
431 	case NativeWindow::MaxSize:
432 		WinWidget->setMaximumSize(vals[i].toSize());
433 		break;
434 	case NativeWindow::Active:
435 		if(vals[i].toBool()){ activate(); } //WinWidget->raiseWindow(); }
436 		break;
437 	/*case NativeWindow::FrameExtents:
438 		qDebug() << " - FRAME CHANGE";
439 		if(vals[i].isNull()){
440 		  vals[i] = QVariant::fromValue<QList<int> >( QList<int>() << WinWidget->geometry().x() << this->width()-WinWidget->geometry().x()-WinWidget->geometry().width() << WinWidget->y() << this->height() - WinWidget->y() - WinWidget->geometry().height() );
441 		  WIN->setProperty(NativeWindow::FrameExtents, vals[i]);
442 		}
443 		qDebug() << "Setting Frame Extents:" << vals[i].value<QList<int> >();
444 		mainLayout->setContentsMargins( vals[i].value< QList<int> >().at(0),vals[i].value< QList<int> >().at(2) - titleLabel->height(),vals[i].value< QList<int> >().at(1),vals[i].value< QList<int> >().at(3));
445 		break;*/
446 	case NativeWindow::WinTypes:
447 		//qDebug() << "Got Window Types:" << vals[i].value< QList<NativeWindow::Type> >();
448 		enableFrame(vals[i].value< QList<NativeWindow::Type> >() );
449 		break;
450 	default:
451 		qDebug() << "Window Property Unused:" << props[i] << vals[i];
452     }
453   }
454 }
455 
456 // === PROTECTED ===
mousePressEvent(QMouseEvent * ev)457 void RootSubWindow::mousePressEvent(QMouseEvent *ev){
458   activate();
459   this->raise();
460   QFrame::mousePressEvent(ev);
461   //qDebug() << "Frame Mouse Press Event";
462   if(activeState != Normal){ return; } // do nothing - already in a state of grabbed mouse
463   offset.setX(0); offset.setY(0);
464   if(ev->button()==Qt::LeftButton){
465     if(this->childAt(ev->pos())!=0){
466       //Clicked on the titlebar
467       startMoving();
468     }else{
469       //Clicked on the frame somewhere
470       startResizing();
471     }
472   }
473 
474 }
475 
mouseMoveEvent(QMouseEvent * ev)476 void RootSubWindow::mouseMoveEvent(QMouseEvent *ev){
477   QFrame::mouseMoveEvent(ev);
478   if(activeState == Normal){
479     setMouseCursor( getStateAtPoint(ev->pos()) ); //just update the mouse cursor
480   }else{
481     //Currently in a modification state
482     QRect geom = this->geometry();
483     QSize minsize(WinWidget->minimumSize().width() + (2*WIN_BORDER), WinWidget->minimumSize().height()+(2*WIN_BORDER)+titleBar->geometry().size().height());
484     switch(activeState){
485       case Move:
486         geom.moveTopLeft(ev->globalPos()-offset); //will not change size
487         break;
488       case ResizeTop:
489         geom.setTop(ev->globalPos().y()-offset.y());
490         if(geom.size().height() < minsize.height()){
491 	  geom.setTop(geom.y() - (minsize.height()-geom.size().height())); //reset back to min height
492         }
493         break;
494       case ResizeTopRight:
495         geom.setTopRight(ev->globalPos()-offset);
496         if(geom.size().height() < minsize.height()){
497 	  geom.setTop(geom.y() - (minsize.height()-geom.size().height())); //reset back to min height
498         }
499         if(geom.size().width() < minsize.width()){
500 	  geom.setRight(geom.x() + minsize.width()); //reset back to min width
501         }
502         break;
503       case ResizeRight:
504         geom.setRight(ev->globalPos().x()-offset.x());
505         if(geom.size().width() < minsize.width()){
506 	  geom.setRight(geom.x() + minsize.width()); //reset back to min width
507         }
508         break;
509       case ResizeBottomRight:
510         geom.setBottomRight(ev->globalPos()-offset);
511         if(geom.size().height() < minsize.height()){
512 	  geom.setBottom(geom.y() + minsize.height()); //reset back to min height
513         }
514         if(geom.size().width() < minsize.width()){
515 	  geom.setRight(geom.x() + minsize.width()); //reset back to min width
516         }
517         break;
518       case ResizeBottom:
519         geom.setBottom(ev->globalPos().y()-offset.y());
520         if(geom.size().height() < minsize.height()){
521 	  geom.setBottom(geom.y() + minsize.height()); //reset back to min height
522         }
523         break;
524       case ResizeBottomLeft:
525         geom.setBottomLeft(ev->globalPos()-offset);
526         if(geom.size().height() < minsize.height()){
527 	  geom.setBottom(geom.y() + minsize.height()); //reset back to min height
528         }
529         if(geom.size().width() < minsize.width()){
530 	  geom.setLeft(geom.x() - (minsize.width()-geom.size().width())); //reset back to min width
531         }
532         break;
533       case ResizeLeft:
534         geom.setLeft(ev->globalPos().x()-offset.x());
535         if(geom.size().width() < minsize.width()){
536 	  geom.setLeft(geom.x() - (minsize.width()-geom.size().width())); //reset back to min width
537         }
538         break;
539       case ResizeTopLeft:
540         geom.setTopLeft(ev->globalPos()-offset);
541         if(geom.size().height() < minsize.height()){
542 	  geom.setTop(geom.y() - (minsize.height()-geom.size().height())); //reset back to min height
543         }
544         if(geom.size().width() < minsize.width()){
545 	  geom.setLeft(geom.x() - (minsize.width()-geom.size().width())); //reset back to min width
546         }
547         break;
548       default:
549 	break;
550     }
551     //if( (geom.width()%2==0 && geom.height()%2==0) || activeState==Move){
552       //qDebug() << " Change Window:" << this->geometry() << geom;
553       if(activeState==Move){ this->setGeometry(geom); }
554       else{
555         //qDebug() << " Change Window Dimensions:" << this->geometry() << geom;
556 	//qDebug() << " - Mouse Pos:" << ev->globalPos() << ev->pos() << "Offset" << offset;
557 	this->setGeometry(geom);
558       }
559     //}
560   }
561 }
562 
mouseReleaseEvent(QMouseEvent * ev)563 void RootSubWindow::mouseReleaseEvent(QMouseEvent *ev){
564   //Check for a right-click event
565   //qDebug() << "Frame Mouse Release Event";
566   QFrame::mouseReleaseEvent(ev);
567   if( (activeState==Normal) && (titleBar->geometry().contains(ev->pos())) && (ev->button()==Qt::RightButton) ){
568     //WinWidget->raiseWindow();//need to ensure the native window is always on top of this frame but under the menu
569     otherM->popup(ev->globalPos());
570     return;
571   }
572   if(activeState!=Normal){
573     if(WinWidget->isPaused()){ WinWidget->resume(); }
574     activeState = Normal;
575     QApplication::restoreOverrideCursor();
576     setMouseCursor( getStateAtPoint(ev->pos()) );
577   }
578   if(QFrame::mouseGrabber() == this){ this->releaseMouse(); }
579   activate();
580   //QTimer::singleShot(0, WinWidget, SLOT(raiseWindow()) );
581 }
582 
583 /*void RootSubWindow::enterEvent(QEvent *ev){
584   QFrame::enterEvent(ev);
585   WinWidget->raiseWindow();
586 }*/
587 /*void RootSubWindow::leaveEvent(QEvent *ev){
588   QFrame::leaveEvent(ev);
589   if(activeState == Normal){
590     setMouseCursor(Normal);
591   }
592   if(!QRect(QPoint(0,0),this->size()).contains( this->mapFromGlobal(QCursor::pos())) ){ WinWidget->lowerWindow(); }
593 }*/
594 
moveEvent(QMoveEvent * ev)595 void RootSubWindow::moveEvent(QMoveEvent *ev){
596   //qDebug() << "Got Move Event:" << ev->pos() << WinWidget->geometry();
597   QFrame::moveEvent(ev);
598   if(!closing && !WinWidget->isPaused()){
599     moveTimer->start();
600   }
601 }
602