1 //===========================================
2 //  Lumina-DE source code
3 //  Copyright (c) 2014-2015, Ken Moore
4 //  Available under the 3-clause BSD license
5 //  See the LICENSE file for full details
6 //===========================================
7 #include "MainUI.h"
8 #include "ui_MainUI.h"
9 
10 #include <LuminaX11.h>
11 #include <QMessageBox>
12 #include <QClipboard>
13 
MainUI()14 MainUI::MainUI()
15   : QMainWindow(), ui(new Ui::MainUI),
16     mousegrabbed(false),
17     picSaved(false),
18     closeOnSave(false)
19 {
20   ui->setupUi(this); //load the designer file
21   XCB = new LXCB();
22   IMG = new ImageEditor(this);
23   ui->scrollArea->setWidget(IMG);
24   areaOverlay = 0;
25   ui->label_zoom_percent->setMinimumWidth( ui->label_zoom_percent->fontMetrics().horizontalAdvance("200%") );
26   setupIcons();
27   ui->spin_monitor->setMaximum(QGuiApplication::screens().count());
28   if(ui->spin_monitor->maximum()<2){
29     ui->spin_monitor->setEnabled(false);
30     ui->radio_monitor->setEnabled(false);
31   }
32   scaleTimer = new QTimer(this);
33     scaleTimer->setSingleShot(true);
34     scaleTimer->setInterval(200); //~1/5 second
35   tabbar = new QTabBar(this);
36     tabbar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
37   ui->tabLayout->insertWidget(0,tabbar, Qt::AlignLeft | Qt::AlignBottom);
38     tabbar->addTab(LXDG::findIcon("view-preview",""), tr("View"));
39     tabbar->addTab(LXDG::findIcon("preferences-other",""), tr("Settings"));
40   ui->stackedWidget->setCurrentWidget(ui->page_current);
41   //Add a spacer in the Toolbar
42   QWidget *spacer = new QWidget(this);
43     spacer->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
44   ui->toolBar->insertWidget(ui->actionClose, spacer);
45   //Setup the connections
46   //connect(ui->tool_save, SIGNAL(clicked()), this, SLOT(saveScreenshot()) );
47   connect(ui->actionSave_As, SIGNAL(triggered()), this, SLOT(saveScreenshot()) );
48   connect(ui->tool_quicksave, SIGNAL(clicked()), this, SLOT(quicksave()) );
49   connect(ui->actionQuick_Save, SIGNAL(triggered()), this, SLOT(quicksave()) );
50   connect(ui->actionClose, SIGNAL(triggered()), this, SLOT(closeApplication()) );
51   //connect(ui->push_snap, SIGNAL(clicked()), this, SLOT(startScreenshot()) );
52   connect(ui->actionTake_Screenshot, SIGNAL(triggered()), this, SLOT(startScreenshot()) );
53   connect(ui->tool_crop, SIGNAL(clicked()), IMG, SLOT(cropImage()) );
54   connect(ui->tool_copy_to_clipboard, SIGNAL(clicked()), this, SLOT(copyToClipboard()) );
55   connect(IMG, SIGNAL(selectionChanged(bool)), this, SLOT(imgselchanged(bool)) );
56   connect(IMG, SIGNAL(scaleFactorChanged(int)), this, SLOT(imgScalingChanged(int)) );
57   connect(ui->slider_zoom, SIGNAL(valueChanged(int)),  this, SLOT(sliderChanged()) );
58   connect(scaleTimer, SIGNAL(timeout()), this, SLOT(imgScalingChanged()) );
59   connect(tabbar, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)) );
60   connect(ui->check_show_popups, SIGNAL(toggled(bool)), this, SLOT(showPopupsChanged(bool)) );
61   settings = LUtils::openSettings("lumina-desktop", "lumina-screenshot",this);
62   ppath = settings->value("previous-path", QDir::homePath()).toString();
63   QString opt = settings->value("screenshot-target", "window").toString();
64   if( opt == "window") {ui->radio_window->setChecked(true); }
65   else if(opt=="area"){ ui->radio_area->setChecked(true); }
66   else{ ui->radio_all->setChecked(true); }
67   ui->spin_delay->setValue(settings->value("screenshot-delay", 0).toInt());
68   ui->check_show_popups->setChecked( settings->value("showPopupWarnings",true).toBool() );
69 
70   ui->tool_resize->setVisible(false); //not implemented yet
71   this->show();
72   IMG->setDefaultSize(ui->scrollArea->maximumViewportSize());
73   IMG->LoadImage( QApplication::screens().at(0)->grabWindow(QApplication::desktop()->winId()).toImage() ); //initial screenshot
74   lastScreenShot = QDateTime::currentDateTime();
75 
76   // Shortcuts
77   quitShortcut = new QShortcut(Qt::CTRL + Qt::Key_Q, this);
78   connect(quitShortcut, SIGNAL(activated()), this, SLOT(quitShortcut_activated()) );
79   openShortcut = new QShortcut(Qt::CTRL + Qt::Key_O, this);
80   connect(openShortcut, SIGNAL(activated()), this, SLOT(quicksave()) );
81 
82   //ui->label_screenshot->setPixmap( cpic.scaled(ui->label_screenshot->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation) );
83 }
84 
~MainUI()85 MainUI::~MainUI(){
86   if(areaOverlay!=0){ areaOverlay->deleteLater(); }
87 }
88 
setupIcons()89 void MainUI::setupIcons(){
90   //Setup the icons
91   ui->tool_quicksave->setIcon( LXDG::findIcon("document-edit","") );
92   ui->actionSave_As->setIcon( LXDG::findIcon("document-save-as","") );
93   ui->actionQuick_Save->setIcon( LXDG::findIcon("document-save","") );
94   ui->actionClose->setIcon( LXDG::findIcon("application-exit","") );
95   ui->tool_copy_to_clipboard->setIcon( LXDG::findIcon("insert-image","") );
96   ui->actionTake_Screenshot->setIcon( LXDG::findIcon("camera-web","") );
97   ui->tool_crop->setIcon( LXDG::findIcon("transform-crop","") );
98   ui->tool_resize->setIcon( LXDG::findIcon("transform-scale","") );
99   this->setWindowIcon( LXDG::findIcon("camera-web","") );
100 }
101 
showSaveError(QString path)102 void MainUI::showSaveError(QString path){
103   QMessageBox::warning(this, tr("Could not save screenshot"), tr("The screenshot could not be saved. Please check directory permissions or pick a different directory")+"\n\n"+path);
104 }
105 
pointsToRect(QPoint pt1,QPoint pt2)106 QRect MainUI::pointsToRect(QPoint pt1, QPoint pt2){
107   QRect rec;
108   if(pt1.x() < pt2.x()){ rec.setLeft(pt1.x()); rec.setRight(pt2.x()); }
109   else{ rec.setLeft(pt2.x()); rec.setRight(pt1.x()); }
110   if(pt1.y() < pt2.y()){ rec.setTop(pt1.y()); rec.setBottom(pt2.y()); }
111   else{ rec.setTop(pt2.y()); rec.setBottom(pt1.y()); }
112   return rec;
113 }
114 
showSBMessage(QString msg,int secs)115 void MainUI::showSBMessage(QString msg, int secs){
116   //Show statusbar message (for designated time)
117   ui->statusBar->showMessage("----> "+msg, secs*1000);
118 }
119 
120 //==============
121 //  PRIVATE SLOTS
122 //==============
saveScreenshot()123 void MainUI::saveScreenshot(){
124   if(mousegrabbed){ return; }
125   QString filepath = QFileDialog::getSaveFileName(this, tr("Save Screenshot"), ppath+"/"+QString( "Screenshot-%1.png" ).arg( lastScreenShot.toString("yyyy-MM-dd-hh-mm-ss")), tr("PNG Files (*.png);;AllFiles (*)") );
126   if(filepath.isEmpty()){
127     closeOnSave = false;
128     return;
129   }
130   if(!filepath.endsWith(".png")){ filepath.append(".png"); }
131   if( !IMG->image().save(filepath, "png") ){
132     closeOnSave = false;
133     showSaveError(filepath);
134   }else{
135     picSaved = true;
136     ppath = filepath.section("/",0,-2); //just the directory
137     settings->setValue("previous-path", ppath);
138     if (closeOnSave) {
139       // We came here from close, now we need to close *after* handling
140       // the current screen event.
141       QTimer::singleShot(0, this, SLOT(close()));
142     }
143   }
144 }
145 
quicksave()146 void MainUI::quicksave(){
147   if(mousegrabbed){ return; }
148     QString savedir = QDir::homePath()+"/";
149     if(QFile::exists(savedir + "Pictures/")){ savedir.append("Pictures/"); }
150     else if(QFile::exists(savedir + "Images/")){ savedir.append("Images/"); }
151 
152     QString path = savedir + QString( "Screenshot-%1.png" ).arg( lastScreenShot.toString("yyyy-MM-dd-hh-mm-ss") );
153     if(IMG->image().save(path, "png") ){
154       picSaved = true;
155       QProcess::startDetached("lumina-open -select \""+path+"\"");
156     }else{
157       showSaveError(path);
158     }
159 
160 }
copyToClipboard()161 void MainUI::copyToClipboard(){
162   //qDebug() << "Copy Image to clipboard";
163   QClipboard *clipboard = QApplication::clipboard();
164   clipboard->setImage(IMG->image());
165   showSBMessage(tr("Image copied to clipboard"), 10);
166   //qDebug() << " - Success:" << !clipboard->image().isNull();
167 }
168 
startScreenshot()169 void MainUI::startScreenshot(){
170   if(mousegrabbed){ return; }
171   lastgeom = this->geometry();
172   if( !getWindow() ){ return; }
173   this->hide();
174   QTimer::singleShot(50+ui->spin_delay->value()*1000, this, SLOT(getPixmap()));
175 }
176 
imgselchanged(bool hassel)177 void MainUI::imgselchanged(bool hassel){
178   ui->tool_crop->setEnabled(hassel);
179   ui->tool_resize->setEnabled(hassel);
180 }
181 
imgScalingChanged(int percent)182 void MainUI::imgScalingChanged(int percent){
183   //qDebug() << "Scale Changed:" << percent;
184   if(percent<0){
185     //Changed by user interaction
186     IMG->setScaling(ui->slider_zoom->value());
187   }else{
188     ui->slider_zoom->setValue(percent);
189   }
190   ui->label_zoom_percent->setText( QString::number(ui->slider_zoom->value())+"%");
191 }
192 
sliderChanged()193 void MainUI::sliderChanged(){
194   ui->label_zoom_percent->setText( QString::number(ui->slider_zoom->value())+"%");
195   scaleTimer->start();
196 }
197 
tabChanged(int tab)198 void MainUI::tabChanged(int tab){
199   if(tab==0){ ui->stackedWidget->setCurrentWidget(ui->page_current); }
200   else{ ui->stackedWidget->setCurrentWidget(ui->page_settings); }
201   ui->frame_modify->setVisible(tab==0);
202 }
203 
showPopupsChanged(bool show)204 void MainUI::showPopupsChanged(bool show){
205   settings->setValue("showPopupWarnings", show);
206 }
207 
getWindow()208 bool MainUI::getWindow(){
209   //Use this function to set cwin
210   cwin = 0;
211   snapArea = QRect(); //clear this too
212   //Save all the current settings for later
213   settings->setValue("screenshot-delay", ui->spin_delay->value());
214   if(ui->radio_window->isChecked()){
215     settings->setValue("screenshot-target", "window");
216     this->grabMouse( QCursor(Qt::CrossCursor) );
217     mousegrabbed = true;
218     this->centralWidget()->setEnabled(false);
219     this->setWindowOpacity(0);
220     return false; //wait for the next click to continue
221   }else  if(ui->radio_area->isChecked()){
222     settings->setValue("screenshot-target", "area");
223     this->grabMouse( QCursor(Qt::CrossCursor) );
224     mousegrabbed = true;
225     this->centralWidget()->setEnabled(false);
226     this->setWindowOpacity(0);
227     return false; //wait for the next click to continue
228   }else if(ui->radio_monitor->isChecked()){
229     //will auto-grab the proper monitor later
230   }else{
231     settings->setValue("screenshot-target", "desktop");
232   }
233   return true;
234 }
235 
getPixmap()236 void MainUI::getPixmap(){
237   QScreen *scrn = QApplication::screens().at(0);
238   QPixmap cpic;
239   //qDebug() << "Grab Pixmap:" << cwin;
240   if( (cwin==0 && ui->radio_window->isChecked() ) || ui->radio_all->isChecked() ){
241     //Grab the whole screen
242     cpic = scrn->grabWindow(QApplication::desktop()->winId());
243   }else if(cwin==0 && ui->radio_monitor->isChecked()){
244     QRect geom = QGuiApplication::screens().at(ui->spin_monitor->value()-1)->availableGeometry();
245     cpic = scrn->grabWindow(QApplication::desktop()->winId(), geom.x(), geom.y(), geom.width(), geom.height() );
246   }else if(cwin==0 && ui->radio_area->isChecked()){
247     //Grab the section of the screen which was selected
248     //qDebug() << "Screen Area:" << snapArea;
249     cpic = scrn->grabWindow(QApplication::desktop()->winId(), snapArea.x(), snapArea.y(), snapArea.width(), snapArea.height() );
250   }else{
251     //Grab just the designated window
252     if(ui->check_frame->isChecked()){
253       QRect geom = XCB->WindowGeometry(cwin, true); //include the frame
254       cpic = scrn->grabWindow(QApplication::desktop()->winId(), geom.x(), geom.y(), geom.width(), geom.height() );
255     }else{
256       cpic = scrn->grabWindow(cwin);
257     }
258   }
259   this->showNormal();
260   this->setGeometry(lastgeom);
261   lastScreenShot = QDateTime::currentDateTime();
262   //Now display the pixmap on the label as well
263   picSaved = false;
264   IMG->LoadImage( cpic.toImage() );
265   tabbar->setCurrentIndex(0);
266 }
mousePressEvent(QMouseEvent * ev)267 void MainUI::mousePressEvent(QMouseEvent *ev){
268   if(mousegrabbed && ui->radio_area->isChecked()){
269     pt_click = ev->globalPos();
270     if(areaOverlay == 0){
271       areaOverlay =  new QWidget(0, Qt::Window | Qt::BypassWindowManagerHint | Qt::WindowStaysOnTopHint);
272       areaOverlay->setWindowOpacity(0.5);
273       areaOverlay->setStyleSheet("background-color: rgba(150,150,150,120)");
274     }
275   }
276   QMainWindow::mouseMoveEvent(ev);
277 }
278 
mouseMoveEvent(QMouseEvent * ev)279 void MainUI::mouseMoveEvent(QMouseEvent *ev){
280   if(mousegrabbed && ui->radio_area->isChecked()){
281     //Not used yet - do something to paint the area so the user can see which area is selected
282     QRect area = pointsToRect(pt_click, ev->globalPos());
283     areaOverlay->setGeometry(area);
284     areaOverlay->show();
285   }else{
286     QMainWindow::mouseMoveEvent(ev);
287   }
288 }
289 
mouseReleaseEvent(QMouseEvent * ev)290 void MainUI::mouseReleaseEvent(QMouseEvent *ev){
291   if(mousegrabbed){
292     mousegrabbed = false;
293     this->centralWidget()->setEnabled(true);
294     this->releaseMouse();
295     this->setWindowOpacity(1);
296     if(ui->radio_area->isChecked()){
297       //Need to determind the rectange which covers the area selected
298       areaOverlay->hide();
299       snapArea = pointsToRect(pt_click, ev->globalPos());
300     }else{
301       //In the middle of selecting a window to take a screenshot
302       //  Get the window underneath the mouse click and take the screenshot
303       QList<WId> wins = XCB->WindowList();
304       QList<WId> stack = XCB->WM_Get_Client_List(true);
305       cwin = 0;
306       //qDebug() << "Try to select window:" << ev->globalPos() << ev->pos() << QCursor::pos();
307       for(int i=stack.length()-1; i>=0 && cwin==0; i--){ //work top->bottom in the stacking order
308         if(!wins.contains(stack[i])){ continue; }
309         if( XCB->WindowGeometry(stack[i], true).contains(ev->globalPos()) && XCB->WindowState(stack[i])!=LXCB::INVISIBLE ){
310           //qDebug() << "Found Window:" << i << XCB->WindowClass(stack[i]) << XCB->WindowGeometry(stack[i], true);
311           cwin = stack[i];
312         }
313       }
314       //qDebug() << " - Got window:" << cwin;
315       if(cwin==this->winId()){  return; } //cancelled
316     }
317     this->hide();
318     QTimer::singleShot(300+ui->spin_delay->value()*1000, this, SLOT(getPixmap()));
319   }else{
320     QMainWindow::mouseReleaseEvent(ev); //normal processing
321   }
322 }
323 
resizeEvent(QResizeEvent *)324 void MainUI::resizeEvent(QResizeEvent*){
325   IMG->setDefaultSize( ui->scrollArea->maximumViewportSize() );
326 }
327 
closeEvent(QCloseEvent * ev)328 void MainUI::closeEvent(QCloseEvent *ev){
329   //qDebug() << "Close Event:" << ui->check_show_popups->isChecked() << picSaved;
330   if(ui->check_show_popups->isChecked() && !picSaved){
331     //Ask what to do about the unsaved changed
332     QMessageBox dialog( QMessageBox::Warning, tr("Unsaved Screenshot"),
333             tr("The current screenshot has not been saved yet. Do you want to save or discard your changes?"),
334             QMessageBox::Discard | QMessageBox::Save | QMessageBox::Cancel, this);
335     dialog.setDefaultButton(QMessageBox::Cancel);
336     dialog.setButtonText(QMessageBox::Save, tr("Save"));
337     dialog.setButtonText(QMessageBox::Discard, tr("Discard"));
338     dialog.setButtonText(QMessageBox::Cancel, tr("Cancel"));
339     switch (dialog.exec()) {
340     case QMessageBox::Discard:
341       // Just close, we don't care about the file.
342       break;
343     case QMessageBox::Save:
344       closeOnSave = true;
345       saveScreenshot();
346       // fall through
347     case QMessageBox::Cancel:
348       ev->ignore();
349       return;
350     }
351   }
352   QMainWindow::closeEvent(ev);
353 }
354 
quitShortcut_activated()355 void MainUI::quitShortcut_activated(){
356     QApplication::quit();
357 }
358