source: src-qt4/pc-softwaremanager/mainUI.cpp @ e6fddc0

releng/10.0.2
Last change on this file since e6fddc0 was e6fddc0, checked in by Ken Moore <ken@…>, 4 months ago

Add the ability for the AppCafe? to set a system flag when the status of available PBI updates changes (using the new SystemFlag? class in libpcbsd).

  • Property mode set to 100644
File size: 54.1 KB
Line 
1/***************************************************************************
2 *   Copyright (C) 2011 - iXsystems                                       *
3 *   kris@pcbsd.org  *
4 *   tim@pcbsd.org   *
5 *   ken@pcbsd.org   *
6 *                                                                         *
7 *   Permission is hereby granted, free of charge, to any person obtaining *
8 *   a copy of this software and associated documentation files (the       *
9 *   "Software"), to deal in the Software without restriction, including   *
10 *   without limitation the rights to use, copy, modify, merge, publish,   *
11 *   distribute, sublicense, and/or sell copies of the Software, and to    *
12 *   permit persons to whom the Software is furnished to do so, subject to *
13 *   the following conditions:                                             *
14 *                                                                         *
15 *   The above copyright notice and this permission notice shall be        *
16 *   included in all copies or substantial portions of the Software.       *
17 *                                                                         *
18 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
19 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
20 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
21 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
22 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
23 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
24 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
25 ***************************************************************************/
26#include "mainUI.h"
27#include "ui_mainUI.h"
28
29MainUI::MainUI(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainUI){
30  //initialization function
31  ui->setupUi(this); //load the Qt-Designer file
32  defaultIcon = ":/application.png";
33  statusLabel = new QLabel();
34  ui->statusbar->addWidget(statusLabel);
35  updatesAvailable = -1; //app starting value (0 for no updates, 1 for updates available)
36}
37
38void MainUI::setWardenMode(QString dir, QString ip){
39  wardenDir = dir; wardenIP = ip;
40}
41
42void MainUI::ProgramInit()
43{ 
44   qDebug("Application starting...");
45   //Initial Checks
46     bool wardenMode = !wardenDir.isEmpty();
47     bool ok = Extras::checkUser(wardenMode);
48     if(!ok && wardenMode){  //Not root
49       QMessageBox::information( this, tr("Error!"), tr("The AppCafe must be run as root when in Warden compatibility mode") );
50       close();     
51     }else if( !ok && !wardenMode){
52       QMessageBox::information( this, tr("Error!"), tr("The AppCafe must be started with user permissions!")+"\n"+tr("The user must also be a part of the \"operator\" group") );
53       close();
54     }
55   
56   //Initialize the backend worker class
57   //qDebug() << "Initialize Backend";
58   PBI = new PBIBackend();
59     if(wardenMode){ PBI->setWardenMode(wardenDir, wardenIP); }
60     
61   //Initialize the Installed tab
62   //qDebug() << "Initialize Installed Tab";
63   initializeInstalledTab();
64   //Initialize the PBI Browser
65   //qDebug() << "Initialize Browser Tab";
66   initializeBrowserTab();
67   
68   //Now startup the backend
69   //qDebug() << "Startup Backend";
70     connect(PBI,SIGNAL(LocalPBIChanges()),this,SLOT(slotRefreshInstallTab()) );
71     connect(PBI,SIGNAL(PBIStatusChange(QString)),this,SLOT(slotPBIStatusUpdate(QString)) );
72     connect(PBI,SIGNAL(RepositoryInfoReady()),this,SLOT(slotEnableBrowser()) );
73     connect(PBI,SIGNAL(NoRepoAvailable()),this,SLOT(slotDisableBrowser()) );
74     connect(PBI,SIGNAL(SearchComplete(QStringList,QStringList)),this,SLOT(slotShowSearchResults(QStringList, QStringList)) );
75     connect(PBI,SIGNAL(SimilarFound(QStringList)),this,SLOT(slotShowSimilarApps(QStringList)) );
76     connect(PBI,SIGNAL(Error(QString,QString,QStringList)),this,SLOT(slotDisplayError(QString,QString,QStringList)) );
77     PBI->start();
78
79   //Make sure we start on the installed tab
80   ui->tabWidget->setCurrentWidget(ui->tab_installed);
81   ui->stackedWidget->setCurrentWidget(ui->page_install_list);
82}
83
84void MainUI::slotSingleInstance(){
85  this->raise();
86  this->showNormal();
87  this->activateWindow();
88}
89
90void MainUI::closeEvent(QCloseEvent *event){
91  bool safe = PBI->safeToQuit();
92  if(!safe){
93    //Verify that they want to continue
94    QMessageBox::StandardButton button = QMessageBox::warning(this, tr("AppCafe Processes Running"), tr("The AppCafe currently has actions pending. Do you want to cancel all running processes and quit anyway?"), QMessageBox::Yes | QMessageBox::Cancel,QMessageBox::Cancel);
95    if(button == QMessageBox::Yes){ //close down
96      PBI->cancelActions( PBI->installedList() ); //close down safely
97    }else{
98      event->ignore();
99      return;
100    }
101  }
102  this->close();
103}
104// ========================
105// ===== MENU OPTIONS =====
106// ========================
107void MainUI::on_actionImport_PBI_List_triggered(){
108  QString file = QFileDialog::getOpenFileName( this, tr("Import PBI File List"), QDir::homePath(), tr("PBI List (*.pbilist)"));
109  if(file.isEmpty()){ return; } //action cancelled
110  bool ok = PBI->importPbiListFromFile(file);
111  if(!ok){ qDebug() << QMessageBox::warning(this,tr("Import Error"),tr("There was an error importing the PBI list")+"\n"+tr("Please make sure that the file has not been corrupted and try again")); }
112}
113
114void MainUI::on_actionExport_PBI_List_triggered(){
115  QString file = QFileDialog::getSaveFileName( this, tr("Export PBI File List"), QDir::homePath()+"/exportfile.pbilist", tr("PBI List (*.pbilist)"));
116  if(file.isEmpty()){ return; } //action cancelled
117  bool ok = PBI->exportPbiListToFile(file);
118  if(!ok){ qDebug() << QMessageBox::warning(this,tr("Export Error"),tr("There was an error exporting the PBI list")+"\n"+tr("Please make sure that you have the proper directory permissions and try again")); }
119}
120
121void MainUI::on_actionQuit_triggered(){
122  this->close();
123}
124
125void MainUI::on_actionAppCafe_Settings_triggered(){
126  PBI->openConfigurationDialog();
127}
128
129void MainUI::on_actionInstall_From_File_triggered(){
130  QStringList files = QFileDialog::getOpenFileNames(this, tr("Install PBI"), QDir::homePath(), tr("PBI Application (*.pbi)") );
131  if(files.isEmpty()){ return; } //cancelled
132  //Verify that they want to install these applications
133  QStringList names;
134  for(int i=0; i<files.length(); i++){ names << files[i].section("/",-1); }
135  names.sort();
136  if( QMessageBox::Yes == QMessageBox::question(this, tr("Verify Installation"), tr("Are you ready to begin installing these PBI's?")+"\n"+tr("NOTE: You will need to manually add desktop/menu icons through the AppCafe afterwards.")+"\n\n"+names.join("\n"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) ){
137    //This might take a while, so don't allow the user to run this action again until it is done (thread safe though)
138    ui->actionInstall_From_File->setEnabled(false);
139    PBI->installPBIFromFile(files);
140    ui->actionInstall_From_File->setEnabled(true);
141  }
142}
143
144// =========================
145// ===== INSTALLED TAB =====
146// =========================
147void MainUI::initializeInstalledTab(){
148       
149  //Setup the action menu for installed applications
150  actionMenu = new QMenu();
151    actionMenu->addAction( QIcon(":icons/view-refresh.png"), tr("Update"), this, SLOT(slotActionUpdate()) );
152    actionMenu->addSeparator();
153    QMenu *dmenu = actionMenu->addMenu( QIcon(":icons/xdg_desktop.png"), tr("Desktop Icons"));
154      dmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddDesktop()) );
155      dmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveDesktop()) );
156    QMenu *mmenu = actionMenu->addMenu( QIcon(":icons/xdg_menu.png"), tr("Menu Icons"));
157      mmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMenu()) );
158      mmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMenu()) ); 
159      mmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMenuAll()) );
160    QMenu *pmenu = actionMenu->addMenu( QIcon(":icons/xdg_paths.png"), tr("Path Links"));
161      pmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddPath()) );
162      pmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemovePath()) ); 
163      pmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddPathAll()) );
164    QMenu *fmenu = actionMenu->addMenu( QIcon(":icons/xdg_mime.png"), tr("File Associations"));
165      fmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMime()) );
166      fmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMime()) ); 
167      fmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMimeAll()) );
168    actionMenu->addSeparator();
169    actionMenu->addAction( QIcon(":icons/remove.png"), tr("Uninstall"), this, SLOT(slotActionRemove()) );
170    actionMenu->addSeparator();
171    actionMenu->addAction( QIcon(":icons/dialog-cancel.png"), tr("Cancel Actions"), this, SLOT(slotActionCancel()) );
172  //Setup the shortcuts menu for installed applications
173  shortcutMenu = new QMenu(this);
174    sDeskMenu = shortcutMenu->addMenu( QIcon(":icons/xdg_desktop.png"), tr("Desktop Icons"));
175      sDeskMenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddDesktop()) );
176      sDeskMenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveDesktop()) );
177    sMenuMenu = shortcutMenu->addMenu( QIcon(":icons/xdg_menu.png"), tr("Menu Icons"));
178      sMenuMenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMenu()) );
179      sMenuMenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMenu()) ); 
180      sMenuMenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMenuAll()) );
181    QMenu *spmenu = shortcutMenu->addMenu( QIcon(":icons/xdg_paths.png"), tr("Path Links"));
182      spmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddPath()) );
183      spmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemovePath()) ); 
184      spmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddPathAll()) );
185    sMimeMenu = shortcutMenu->addMenu( QIcon(":icons/xdg_mime.png"), tr("File Associations"));
186      sMimeMenu ->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMime()) );
187      sMimeMenu ->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMime()) ); 
188      sMimeMenu ->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMimeAll()) );
189  ui->tool_install_shortcuts->setMenu(shortcutMenu);
190  //Setup the binary menu for installed applications
191  appBinMenu = new QMenu();
192  ui->tool_install_startApp->setMenu(appBinMenu);
193    connect(appBinMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotStartApp(QAction*)) );
194  //Initialize the context menu
195  contextActionMenu = new QMenu(this);
196    connect(contextActionMenu, SIGNAL(aboutToHide()), this, SLOT(contextMenuFinished()) );
197  //Now setup the action button
198  ui->tool_install_performaction->setMenu(actionMenu);
199  ui->tool_install_performaction->setPopupMode(QToolButton::InstantPopup);
200  //Now setup any defaults for the installed tab
201  ui->tree_install_apps->setIconSize(QSize(22,22));
202  ui->tree_install_apps->setContextMenuPolicy(Qt::CustomContextMenu);
203  connect(ui->tree_install_apps, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(slotCheckSelectedItems()) );
204  connect(ui->tree_install_apps, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT( slotInstalledAppRightClicked(const QPoint &)) );
205  slotRefreshInstallTab();
206}
207
208void MainUI::formatInstalledItemDisplay(QTreeWidgetItem *item){
209  //simplification function for filling the tree widget item with the appropriate information about the PBI
210  QString ID = item->whatsThis(0);
211  if(item->text(0).isEmpty()){  //new entry - get everything
212    QStringList vals; vals <<"name"<<"version"<<"status"; //same as column order
213    if(!vals.isEmpty()){
214      vals = PBI->PBIInfo(ID, vals);
215      for(int i=0; i<vals.length(); i++){ item->setText(i,vals[i]); }
216      QString icon = PBI->PBIInfo(ID, QStringList("icon")).join("");
217        //Load a default icon if none found
218      if(icon.isEmpty()){ icon = defaultIcon; }
219      item->setIcon(0,QIcon(icon) );
220      item->setCheckState(0,Qt::Unchecked);
221    }
222  }else{ // Just update the necesary info
223    QStringList vals; vals << "version" << "status";
224    vals = PBI->PBIInfo(ID,vals);
225    if(!vals.isEmpty()){ item->setText(1,vals[0]); item->setText(2,vals[1]); }
226  }
227}
228
229QStringList MainUI::getCheckedItems(){
230  //Return the pbiID's of all the active items
231  QStringList output;
232  //See if we are on the single-app details page or custom context menu- then get the current app only
233  if(ui->stackedWidget->currentWidget() == ui->page_install_details){
234      output << cDetails; 
235         
236  //Check for whether this is the context menu on the main widget
237  }else if(!cDetails.isEmpty()){
238     output << cDetails;
239     cDetails.clear();
240         
241  //If on the main Installed page, look for checked items only
242  }else{
243    for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
244      if(ui->tree_install_apps->topLevelItem(i)->checkState(0) == Qt::Checked){
245        output << ui->tree_install_apps->topLevelItem(i)->whatsThis(0);
246      }
247    }
248  }
249  //qDebug() << "Checked Items:" << output;
250  return output;       
251}
252
253// === SLOTS ===
254void MainUI::slotRefreshInstallTab(){
255  //Update the list of installed PBI's w/o clearing the list (loses selections)
256   //Get the list we need (in order)
257  QStringList installList = PBI->installedList();
258  installList.sort(); //sort in ascending order
259   //Get the list we have now
260  QStringList cList;
261  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
262    cList << ui->tree_install_apps->topLevelItem(i)->whatsThis(0);
263  }
264  bool up = false; //to keep track of whether updates are available for any installed apps
265  //Quick finish if no items installed
266  if(installList.isEmpty()){
267    ui->tree_install_apps->clear();
268  }else{
269    //Now make adjustments as necessary
270    for(int i=0; i<installList.length(); i++){
271      //Detemine what action should be done with this item location
272      int todo = 0; //0=insert new item, 1=update current item, 2=remove current item
273      if(i < cList.length()){
274        if(installList[i] == cList[i]){ todo=1; }
275        else if( installList.contains(cList[i]) && !cList.contains(installList[i]) ){ todo=0; } //new item to be inserted here
276        else if( !installList.contains(cList[i]) ){ todo=2; } //current item in this location should be removed
277      }
278      //Now perform the action on this location
279      if(todo==0){ 
280        //insert new item
281        QTreeWidgetItem *item = new QTreeWidgetItem; //create the item
282        item->setWhatsThis(0,installList[i]);
283        //Now format the display
284        formatInstalledItemDisplay(item);
285        if(item->text(0).isEmpty()){
286          //Do not put empty items into the display
287          delete item;
288        }else{
289          //Now insert this item onto the list
290          ui->tree_install_apps->insertTopLevelItem(i,item);
291          cList.insert(i,installList[i]); //reflect this inclusion into the current list
292        }
293        up = up || !PBI->upgradeAvailable(installList[i]).isEmpty(); //will remain "true" if a single item has an update available
294      }else if(todo==1){
295        //Update current item
296        formatInstalledItemDisplay( ui->tree_install_apps->topLevelItem(i) );
297        up = up || !PBI->upgradeAvailable(installList[i]).isEmpty(); //will remain "true" if a single item has an update available
298      }else{
299        //Remove current item
300        ui->tree_install_apps->takeTopLevelItem(i);
301        cList.removeAt(i); //reflect the change to the current list
302        i--; //Re-check the item that should be in this location
303      }
304    }
305    //Now makesure that there are no extra items at the end
306    int il = installList.length();
307    while(il < cList.length()){
308      ui->tree_install_apps->takeTopLevelItem(il);
309      cList.removeAt(il); //reflect the change to the current list
310    }
311  } //end of empty list check
312 
313  //Check whether the system needs to be notified about a new PBI update availability
314  if( (updatesAvailable==0 && up) || (updatesAvailable==1 && !up) ){
315    SystemFlags::setFlag(SystemFlags::PbiUpdate);
316  }
317  if(up){ updatesAvailable = 1; }
318  else{ updatesAvailable = 0; }
319  //Make sure that there is an item selected
320  if(ui->tree_install_apps->topLevelItemCount() > 0 ){
321    if( ui->tree_install_apps->selectedItems().isEmpty() ){
322      ui->tree_install_apps->setCurrentItem( ui->tree_install_apps->topLevelItem(0) );
323    }
324    //Now re-size the columns to the minimum required width
325    for(int i=0; i<3; i++){
326      ui->tree_install_apps->resizeColumnToContents(i);
327    } 
328  }
329  //If the installed app page is visible, be sure to update it too
330  if( ui->stackedWidget->currentWidget() == ui->page_install_details){
331    updateInstallDetails(cDetails);
332  }
333  slotUpdateSelectedPBI();; //Update the info boxes
334  slotDisplayStats();
335  slotCheckSelectedItems();
336  //If the browser app page is currently visible for this app
337  if( (ui->stacked_browser->currentWidget() == ui->page_app) && ui->page_app->isVisible() ){
338    slotGoToApp(cApp);
339  }
340}
341
342void MainUI::slotCheckSelectedItems(){
343  bool chkd = false;
344  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
345    if(ui->tree_install_apps->topLevelItem(i)->checkState(0) == Qt::Checked){
346      chkd = true; break;
347    }
348  }
349  ui->tool_install_performaction->setEnabled(chkd);
350  if(ui->stackedWidget->currentWidget() != ui->page_install_details){
351    cDetails.clear(); //Make sure this is cleared if not on the details page
352  }
353}
354
355void MainUI::slotPBIStatusUpdate(QString pbiID){
356  //This will do a full update of a particlar PBI entry
357  //    and just update/check the icons for all the other PBI's
358  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
359    QString itemID = ui->tree_install_apps->topLevelItem(i)->whatsThis(0);
360    if(itemID == pbiID){
361      QString stat = PBI->PBIInfo(pbiID,QStringList()<<"status").join("");
362      ui->tree_install_apps->topLevelItem(i)->setText(2,stat);
363      // See if we need to update the download progress bar
364      QString appID = ui->tree_install_apps->currentItem()->whatsThis(0);
365      if ( appID == pbiID ) {
366        slotUpdateSelectedPBI();
367        //If the details page is currently visible, update it too
368        if(ui->stackedWidget->currentWidget() == ui->page_install_details){
369          updateInstallDetails(appID);
370        }
371      }
372    }else{
373      //Just check/update the icon if necessary
374      if(ui->tree_install_apps->topLevelItem(i)->icon(0).isNull()){
375         ui->tree_install_apps->topLevelItem(i)->setIcon(0, QIcon( PBI->PBIInfo(itemID, QStringList() << "icon").join("") ));
376      }
377    }
378  }
379  //If the browser app page is current for this app
380  QString metaID = PBI->pbiToAppID(pbiID);
381  if( (ui->stacked_browser->currentWidget() == ui->page_app) && (cApp == metaID) && ui->page_app->isVisible() ){
382    slotUpdateAppDownloadButton();
383  }
384}
385
386void MainUI::on_tool_install_details_clicked(){
387  //Get the current item
388  QString appID;
389  if(ui->tree_install_apps->topLevelItemCount() > 0){
390    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
391  }
392  if(appID.isEmpty()){return;}
393  //Update the info on the details page
394  updateInstallDetails(appID);
395  //Now show the page
396  ui->stackedWidget->setCurrentWidget(ui->page_install_details);
397}
398
399void MainUI::on_tool_install_back_clicked(){
400  //List page should always be current based upon backend
401  ui->stackedWidget->setCurrentWidget(ui->page_install_list);
402}
403
404void MainUI::on_tool_install_gotobrowserpage_clicked(){
405  //When you want to open up the browser page for an application
406  QString appID = Extras::nameToID( ui->tree_install_apps->currentItem()->text(0) );
407  slotGoToApp(appID);
408}
409
410void MainUI::on_tool_install_toggleall_clicked(){
411  //Determine if the current item is checked or unchecked
412  bool checkall = (ui->tree_install_apps->currentItem()->checkState(0) == Qt::Unchecked);
413  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
414    if(checkall){ ui->tree_install_apps->topLevelItem(i)->setCheckState(0,Qt::Checked); }
415    else{ui->tree_install_apps->topLevelItem(i)->setCheckState(0,Qt::Unchecked); }
416  }
417  slotCheckSelectedItems();
418}
419
420void MainUI::on_tree_install_apps_itemSelectionChanged(){
421  //When an installed PBI is clicked on
422  slotUpdateSelectedPBI();
423  if(ui->stackedWidget->currentWidget() != ui->page_install_list){
424    ui->stackedWidget->setCurrentWidget(ui->page_install_list);
425  }
426  slotCheckSelectedItems();
427}
428
429void MainUI::on_tree_install_apps_itemDoubleClicked(QTreeWidgetItem *item){
430 //Make sure it is a valid/installed application
431 QString appID = item->whatsThis(0);
432  appID = PBI->isInstalled(appID);
433  if(appID.isEmpty()){ return; } //invalid item
434  //Update the info on the details page
435  updateInstallDetails(appID);
436  //Now show the page
437  ui->stackedWidget->setCurrentWidget(ui->page_install_details);
438}
439
440void MainUI::on_check_install_autoupdate_clicked(){
441  //Get the current item
442  QString appID;
443  if(ui->tree_install_apps->topLevelItemCount() > 0){
444    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
445  }
446  if(appID.isEmpty()){return;}
447  //Now determine the current state of the checkbox
448  bool enabled = ui->check_install_autoupdate->isChecked();
449  //Now have the backend make the change
450  PBI->enableAutoUpdate(appID, enabled);
451  //Now ask if the user also wants to start updating it now
452  if(enabled && !PBI->upgradeAvailable(appID).isEmpty()){
453    if( QMessageBox::Yes == QMessageBox::question(this, tr("Start Update?"), tr("Do you wish to start updating this application right now?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) ){
454      PBI->upgradePBI(QStringList() << appID);
455    }
456  }
457  //Now force the info on the page to be updated in a moment (need time for database to update)
458  //QTimer::singleShot(500, this, SLOT(on_tool_install_details_clicked()) );
459}
460
461void MainUI::on_tool_install_update_clicked(){
462  //Get the current item
463  QString appID;
464  if(ui->tree_install_apps->topLevelItemCount() > 0){
465    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
466  }
467  if(appID.isEmpty()){return;}
468  PBI->upgradePBI(QStringList() << appID);
469}
470
471void MainUI::on_tool_install_remove_clicked(){
472  //Get the current item
473  QString appID;
474  if(ui->tree_install_apps->topLevelItemCount() > 0){
475    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
476  }
477  if(appID.isEmpty()){return;}
478  //Verify removal
479  if( QMessageBox::Yes == QMessageBox::question(this,tr("Verify PBI Removal"), tr("Are you sure you wish to remove this application?")+"\n\n"+appID,QMessageBox::Yes | QMessageBox::Cancel,QMessageBox::Cancel) ){
480    PBI->removePBI(QStringList() << appID);
481  }
482}
483
484void MainUI::on_tool_install_cancel_clicked(){
485  //Get the current item
486  QString appID;
487  if(ui->tree_install_apps->topLevelItemCount() > 0){
488    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
489  }
490  if(appID.isEmpty()){return;}
491  PBI->cancelActions(QStringList() << appID);
492 
493}
494
495void MainUI::on_tool_install_maintainer_clicked(){
496  //Get the current item
497  QString appID;
498  if(ui->tree_install_apps->topLevelItemCount() > 0){
499    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
500  }
501  if(appID.isEmpty()){return;}
502  //Get the maintainer email
503  QString email = PBI->PBIInfo(appID, QStringList() << "maintainer").join("");
504  if(email.isEmpty()){ return; }
505  //Verify that the user wants to launch their email client
506  if(QMessageBox::Yes != QMessageBox::question(this, tr("Launch Email Client?"), tr("Do you want to try launching your default email client? \n You must have this setup within your current desktop environment for this to work properly. If not, you can send an email to the address below manually.")+"\n\n"+email, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) ){
507    return;
508  }
509  qDebug() << "Launching email to:" << email;
510  //Get the command from the action
511  QString cmd = "mailto:"+email;
512  QStringList info = PBI->PBIInfo(appID, QStringList() << "name" << "date" << "version" << "arch" << "fbsdversion" );
513  //Add a sample subject
514  cmd.append("?subject="+info[0]+" port question");
515  //Add the info to the body of the email
516  cmd.append("&body=");
517  cmd.append("-----------\nPBI Information:\nName: "+info[0] + "\nDate Created: "+info[1] +"\nVersion: "+info[2] +"\nArchitecture: "+info[3] +"\nFreeBSD Version: "+info[4] );
518  //Startup the command externally
519  QProcess::execute("xdg-open \""+cmd+"\"");
520}
521
522void MainUI::slotInstalledAppRightClicked(const QPoint &pt){
523  //Get the item under the mouse click
524  QTreeWidgetItem *it = ui->tree_install_apps->itemAt(pt);
525  if(it==0){ return; } // no item selected
526  QString pbiID = it->whatsThis(0);
527  qDebug() << "Get context menu for:" << pbiID;
528  //Now Update the context menu appropriately
529  QStringList info = PBI->PBIInfo(pbiID, QStringList() << "hasdesktopicons" << "hasmenuicons" << "hasmimetypes");
530  if(info.isEmpty()){ return; } //invalid application
531  bool pending = PBI->isWorking(pbiID);
532  contextActionMenu->clear();
533  if(!PBI->upgradeAvailable(pbiID).isEmpty() &&  !pending){
534    //Upgrade is only available if actions not pending
535    contextActionMenu->addAction( QIcon(":icons/view-refresh.png"), tr("Update"), this, SLOT(slotActionUpdate()) );
536    contextActionMenu->addSeparator();
537  }
538  if(info[0]=="true"){
539    QMenu *dmenu = contextActionMenu->addMenu( QIcon(":icons/xdg_desktop.png"), tr("Desktop Icons"));
540      dmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddDesktop()) );
541      dmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveDesktop()) );
542  }
543  if(info[1]=="true"){
544    QMenu *mmenu = contextActionMenu->addMenu( QIcon(":icons/xdg_menu.png"), tr("Menu Icons"));
545      mmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMenu()) );
546      mmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMenu()) ); 
547      mmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMenuAll()) );
548  }
549  //Paths are always available if actually installed
550  if( !PBI->isInstalled(pbiID).isEmpty() ){
551    QMenu *pmenu = contextActionMenu->addMenu( QIcon(":icons/xdg_paths.png"), tr("Path Links"));
552      pmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddPath()) );
553      pmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemovePath()) ); 
554      pmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddPathAll()) );
555  }
556  if(info[2]=="true"){
557    QMenu *fmenu = contextActionMenu->addMenu( QIcon(":icons/xdg_mime.png"), tr("File Associations"));
558      fmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMime()) );
559      fmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMime()) ); 
560      fmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMimeAll()) );
561  }
562  if(!pending){
563    //Remove option is only available if not currently pending actions
564    contextActionMenu->addSeparator();
565    contextActionMenu->addAction( QIcon(":icons/remove.png"), tr("Uninstall"), this, SLOT(slotActionRemove()) );
566  }
567  if(pending){
568    //Cancel option is only available if actions are currently pending   
569    contextActionMenu->addSeparator();
570    contextActionMenu->addAction( QIcon(":icons/dialog-cancel.png"), tr("Cancel Actions"), this, SLOT(slotActionCancel()) );
571  }
572  //Now show the menu
573  cDetails = pbiID; //save this so we know which app is currently being modified
574  contextActionMenu->popup(ui->tree_install_apps->mapToGlobal(pt));
575}
576
577void MainUI::contextMenuFinished(){
578  QTimer::singleShot(500, this, SLOT(slotCheckSelectedItems()) );       
579}
580
581// === SELECTED PBI ACTIONS ===
582void MainUI::slotActionAddDesktop(){
583  QStringList items = getCheckedItems();
584  PBI->addDesktopIcons(items,FALSE); //only for current user
585}
586
587void MainUI::slotActionRemoveDesktop(){
588  QStringList items = getCheckedItems();
589  PBI->rmDesktopIcons(items,FALSE);  //Only for current user
590}
591
592void MainUI::slotActionAddPath(){
593  QStringList items = getCheckedItems();
594  PBI->addPaths(items,FALSE);  //Only for current user 
595}
596
597void MainUI::slotActionRemovePath(){
598  QStringList items = getCheckedItems();
599  PBI->rmPaths(items,FALSE);  //Only for current user
600}
601
602void MainUI::slotActionAddPathAll(){
603  QStringList items = getCheckedItems();
604  PBI->addPaths(items,TRUE);  //For all users (root permissions required)
605}
606
607void MainUI::slotActionAddMenu(){
608  QStringList items = getCheckedItems();
609  PBI->addMenuIcons(items,FALSE);  //Only for current user
610}
611
612void MainUI::slotActionRemoveMenu(){
613  QStringList items = getCheckedItems();
614  PBI->rmMenuIcons(items,FALSE);  //Only for current user       
615}
616
617void MainUI::slotActionAddMenuAll(){
618  QStringList items = getCheckedItems();
619  PBI->addMenuIcons(items,TRUE);  //For all users (root permissions required)   
620}
621
622void MainUI::slotActionAddMime(){
623  QStringList items = getCheckedItems();
624  PBI->addMimeTypes(items,FALSE);  //Only for current user     
625}
626
627void MainUI::slotActionRemoveMime(){
628  QStringList items = getCheckedItems();
629  PBI->rmMimeTypes(items,FALSE);  //Only for current user       
630}
631
632void MainUI::slotActionAddMimeAll(){
633    QStringList items = getCheckedItems();
634  PBI->addMimeTypes(items,TRUE);  //For all users (root permissions required)   
635}
636
637void MainUI::slotActionUpdate(){
638  QStringList checkedID = getCheckedItems();
639  if(!checkedID.isEmpty()){
640    PBI->upgradePBI(checkedID); 
641  }
642}
643
644void MainUI::slotActionRemove(){
645  QStringList checkedID = getCheckedItems();
646  if(!checkedID.isEmpty()){
647    //Verify that the user really wants to remove these apps
648    if( QMessageBox::Yes == QMessageBox::question(this,tr("Verify PBI Removal"), tr("Are you sure you wish to remove these applications?")+"\n\n"+checkedID.join("\n"),QMessageBox::Yes | QMessageBox::Cancel,QMessageBox::Cancel) ){
649      PBI->removePBI(checkedID);
650    }
651  }
652}
653
654void MainUI::slotActionCancel(){
655  QStringList checkedID = getCheckedItems();
656  if(!checkedID.isEmpty()){
657    PBI->cancelActions(checkedID); 
658  }
659}
660
661void MainUI::slotStartApp(QAction* act){
662  qDebug() << "Starting external application:" << act->text();
663  //Get the command from the action
664  QString desktopfile = act->whatsThis();
665  QString cmd = "xdg-open "+desktopfile;
666  //Startup the command externally
667  QProcess::execute(cmd);
668}
669
670void MainUI::slotUpdateSelectedPBI(){
671  //Get the currently selected app
672  QString appID;
673  if(ui->tree_install_apps->topLevelItemCount() > 0){
674    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
675  }
676  //See if this one is actually installed
677  appID = PBI->isInstalled(appID);
678  if(appID.isEmpty()){
679    ui->tool_install_details->setEnabled(false);
680  }else{
681    ui->tool_install_details->setEnabled(true);   
682  }
683   
684}
685
686void MainUI::updateInstallDetails(QString appID){
687  //Get the information to update the details page
688  //Get the PBI info for that item
689    cDetails = appID; //save for later
690    QStringList vals; 
691    vals << "name" << "icon" << "author" << "website" << "version" << "license" << "description" << "maintainer" << "date" << "arch";
692    QStringList bools;
693    bools << "autoupdate" << "hasdesktopicons" << "hasmenuicons" << "requiresroot" << "hasmimetypes";
694    vals = PBI->PBIInfo(appID,vals);
695    bools = PBI->PBIInfo(appID,bools);
696    //Make sure the info lists are not empty
697    if(vals.isEmpty() || bools.isEmpty()){
698      ui->tool_install_details->setEnabled(false);
699      return; 
700    }
701    //Load a default icon if none found
702    if(vals[1].isEmpty()){ vals[1] = defaultIcon; }
703    //Now set the info on the UI
704    bool desktopSC = (bools[1] == "true"); // XDG desktop entries available
705    bool menuSC= (bools[2] == "true");   // XDG menu entries available
706    bool autoupdate = (bools[0] == "true"); //auto-update enabled
707    bool rootonly = (bools[3] == "true"); //requires root
708    bool mimeSC = (bools[4] == "true"); //XDG mime entries available
709    //Create the shortcuts string
710    sDeskMenu->setEnabled(desktopSC);
711    sMenuMenu->setEnabled(menuSC);
712    sMimeMenu->setEnabled(mimeSC);
713    //Now display that info on the UI
714    ui->label_install_app->setText(vals[0]);
715    ui->label_install_icon->setPixmap( QPixmap(vals[1]).scaled(64,64, Qt::KeepAspectRatio, Qt::SmoothTransformation) );
716    if(vals[3].isEmpty()){ 
717      ui->label_install_author->setText(vals[2]); 
718      ui->label_install_author->setToolTip("");
719    }else{ 
720      ui->label_install_author->setText("<a href="+vals[3]+">"+vals[2]+"</a>"); 
721      ui->label_install_author->setToolTip(vals[3]); //show website URL as tooltip
722    }
723    ui->label_install_version->setText(vals[4]);
724    ui->label_install_license->setText(vals[5]);
725    ui->text_install_description->setPlainText(vals[6]);
726    ui->tool_install_maintainer->setVisible( vals[7].contains("@") );
727    ui->label_install_date->setText(vals[8]);
728    ui->label_install_arch->setText(vals[9]);
729    //ui->label_install_shortcuts->setText(shortcuts);
730    ui->check_install_autoupdate->setChecked(autoupdate);
731 
732    //Adjust the quick action buttons as necessary
733    if( PBI->isWorking(appID) ){
734      //Actions pending/working only show cancel button
735      ui->tool_install_cancel->setVisible(TRUE);
736      ui->tool_install_remove->setVisible(FALSE);
737      ui->tool_install_update->setVisible(FALSE);
738      ui->tool_install_startApp->setVisible(FALSE);
739    }else{
740      //Nothing pending
741      ui->tool_install_cancel->setVisible(FALSE);
742      if( !PBI->isInstalled(appID).isEmpty() ){ 
743        //Remove Button
744        ui->tool_install_remove->setVisible(TRUE);
745        if(rootonly){ ui->tool_install_remove->setIcon(QIcon(":icons/remove-root.png")); }
746        else{ ui->tool_install_remove->setIcon(QIcon(":icons/remove.png")); }
747        //Update
748        if(PBI->upgradeAvailable(appID).isEmpty()){ ui->tool_install_update->setVisible(FALSE); }
749        else{
750          ui->tool_install_update->setVisible(TRUE); 
751          if(rootonly){ ui->tool_install_update->setIcon(QIcon(":icons/app_upgrade_small-root.png")); }
752          else{ ui->tool_install_update->setIcon(QIcon(":icons/app_upgrade_small.png")); }
753        }
754        //Start Application binaries
755        QStringList bins = PBI->pbiBinList(appID);
756        appBinMenu->clear();
757        for(int i=0; i<bins.length(); i++){
758          QAction *act = new QAction(this);
759            act->setText(bins[i].section("::::",0,0)); //set name
760            act->setWhatsThis(bins[i].section("::::",1,10)); //set command string
761          appBinMenu->addAction(act);
762        }
763        if(appBinMenu->isEmpty()){ ui->tool_install_startApp->setVisible(FALSE); }
764        else{ ui->tool_install_startApp->setVisible(TRUE); }
765      }else{ 
766        ui->tool_install_remove->setVisible(FALSE); 
767        ui->tool_install_update->setVisible(FALSE); 
768        ui->tool_install_startApp->setVisible(FALSE);
769      }   
770    }
771
772  //Update the current status indicators
773  QString stat = PBI->currentAppStatus(appID,true); //get the raw status
774  QString statF = PBI->currentAppStatus(appID, false); //get the non-raw status
775  if( stat.isEmpty() && statF.isEmpty() ){
776    //Not currently running - hide the display indicators
777    ui->group_install_appStat->setVisible(false);
778  }else{
779    //Currently installing/removing/updating - show last message from process
780    if(!statF.isEmpty()){ ui->label_install_status->setText(statF); }
781    else{ ui->label_install_status->setText(stat); }
782    ui->group_install_appStat->setVisible(TRUE);
783  }
784}
785
786// ==========================
787// ====== BROWSER TAB =======
788// ==========================
789void MainUI::initializeBrowserTab(){
790  ui->tab_browse->setEnabled(FALSE);
791  //Always make sure that the browser starts off on the "home" page
792  ui->stacked_browser->setCurrentWidget(ui->page_home);
793  //With invisible shortcut buttons
794  ui->tool_browse_cat->setVisible(FALSE);
795  ui->tool_browse_app->setVisible(FALSE);
796  //Clear any items left over from the designer form
797  clearScrollArea(ui->scroll_br_home_newapps);
798  clearScrollArea(ui->scroll_br_home_rec);
799  //Search functionality
800  searchTimer = new QTimer();
801    searchTimer->setSingleShot(TRUE);
802    searchTimer->setInterval(300); // 0.3 sec wait before a search
803    connect(searchTimer,SIGNAL(timeout()),this,SLOT(slotGoToSearch()) );
804  connect(ui->tool_browse_search,SIGNAL(clicked()),this,SLOT(slotGoToSearch()) );
805  connect(ui->line_browse_searchbar,SIGNAL(returnPressed()),this,SLOT(slotGoToSearch()) );
806  connect(ui->tool_browse_gotocat, SIGNAL(clicked()), this, SLOT(slotGoToCatBrowser()) );
807}
808
809// === SLOTS ===
810void MainUI::slotDisableBrowser(bool shownotification){
811  if(shownotification){ qDebug() << "No Repo Available: De-activating the Browser"; }
812  ui->tabWidget->setCurrentWidget(ui->tab_installed);
813  ui->tab_browse->setEnabled(FALSE);
814  slotDisplayStats();
815}
816
817void MainUI::slotEnableBrowser(){
818  qDebug() << "Repo Ready: - generating browser home page";
819  //Now create the browser home page
820  slotUpdateBrowserHome();
821  //And allow the user to go there
822  ui->tab_browse->setEnabled(TRUE);
823  slotDisplayStats();
824}
825
826void MainUI::slotUpdateBrowserHome(){
827  //Load the Recommendations
828  clearScrollArea(ui->scroll_br_home_rec);
829  QVBoxLayout *reclayout = new QVBoxLayout;
830  QStringList recList = PBI->getRecommendations();
831  QStringList info; info << "name" << "shortdescription" << "icon" << "type";
832  for(int i=0; i<recList.length(); i++){
833    QStringList data = PBI->AppInfo(recList[i],info);
834    if(!data.isEmpty()){
835      LargeItemWidget *item = new LargeItemWidget(recList[i],data[0],data[1],data[2]);
836      //Set the type icon
837      item->setType(data[3].toLower());
838      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
839      reclayout->addWidget(item);
840    }
841  }
842  reclayout->addStretch(); //add a spacer to the end
843  ui->scroll_br_home_rec->widget()->setLayout(reclayout);
844  //Load the newest applications
845  clearScrollArea(ui->scroll_br_home_newapps);
846  QHBoxLayout *newapplayout = new QHBoxLayout;
847  QStringList newapps = PBI->getRecentApps();
848  for(int i=0; i<newapps.length(); i++){
849    QStringList appdata = PBI->AppInfo(newapps[i],QStringList() << "name" << "icon" << "latestversion");
850    if(!appdata.isEmpty()){
851      SmallItemWidget *item = new SmallItemWidget(newapps[i],appdata[0],appdata[1],appdata[2]);
852      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
853      newapplayout->addWidget(item);
854    }
855  }
856  newapplayout->addStretch(); //add a spacer to the end
857  newapplayout->setContentsMargins(0,0,0,0);
858  newapplayout->setSpacing(0);
859  ui->scroll_br_home_newapps->widget()->setLayout(newapplayout);
860  //Make sure that the newapps scrollarea is the proper fit vertically (no vertical scrolling)
861  ui->scroll_br_home_newapps->setMinimumHeight(ui->scroll_br_home_newapps->widget()->minimumSizeHint().height());
862 
863  //Make sure the new apps area is invisible if no items available
864  if(newapps.isEmpty()){ ui->group_br_home_newapps->setVisible(FALSE); }
865  else{ ui->group_br_home_newapps->setVisible(TRUE); }
866  //make sure the home page is visible in the browser (slotGoToHome without changing tabs)
867  ui->stacked_browser->setCurrentWidget(ui->page_home); 
868  //Make sure the shortcut buttons are disabled
869  ui->tool_browse_cat->setVisible(FALSE);
870  ui->tool_browse_app->setVisible(FALSE); 
871 
872  //Now update the category browser page (since it only needs to be done once like the home menu)
873  //Load the Categories
874  QStringList cats = PBI->browserCategories();
875    cats.sort();
876    clearScrollArea(ui->scroll_br_cats);
877    QVBoxLayout *catlayout = new QVBoxLayout;
878    info.clear(); info << "name" << "description" << "icon";
879    for(int i=0; i<cats.length(); i++){
880      QStringList data = PBI->CatInfo(cats[i],info);
881      if(!data.isEmpty()){
882        LargeItemWidget *item = new LargeItemWidget(cats[i],data[0],data[1],data[2]);
883        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToCategory(QString)) );
884        catlayout->addWidget(item);
885      }
886    }
887    catlayout->addStretch(); //add a spacer to the end
888    ui->scroll_br_cats->widget()->setLayout(catlayout);
889}
890
891void MainUI::slotGoToHome(){
892  ui->tabWidget->setCurrentWidget(ui->tab_browse);
893  ui->stacked_browser->setCurrentWidget(ui->page_home); 
894  //Make sure the shortcut buttons are disabled
895  ui->tool_browse_cat->setVisible(false);
896  ui->tool_browse_app->setVisible(false);
897  ui->tool_browse_gotocat->setVisible(true);
898}
899
900void MainUI::slotGoToCatBrowser(){
901  ui->tabWidget->setCurrentWidget(ui->tab_browse);
902  ui->stacked_browser->setCurrentWidget(ui->page_browsecats);
903  //Make sure the shortcut buttons are diabled
904  ui->tool_browse_cat->setVisible(false);
905  ui->tool_browse_app->setVisible(false);
906  ui->tool_browse_gotocat->setVisible(false);
907}
908
909void MainUI::slotGoToCategory(QString cat){
910  qDebug() << "Show Category:" << cat;
911  //Get the apps in this category
912  QStringList apps = PBI->browserApps(cat);
913  if(apps.isEmpty()){
914    qDebug() << "Invalid category:" << cat;
915    return;
916  }
917  clearScrollArea(ui->scroll_br_cat_apps);
918  apps.sort();
919  QVBoxLayout *applayout = new QVBoxLayout;
920  QStringList info; info << "name" << "shortdescription" << "icon" << "type";
921  for(int i=0; i<apps.length(); i++){
922    QStringList data = PBI->AppInfo(apps[i],info);
923    if(!data.isEmpty()){
924      //Trim the description if necessary
925      if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
926      LargeItemWidget *item = new LargeItemWidget(apps[i],data[0],data[1],data[2]);
927      item->setType(data[3].toLower());
928      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
929      applayout->addWidget(item); 
930    }
931  }
932  applayout->addStretch();
933  ui->scroll_br_cat_apps->widget()->setLayout(applayout);
934  //Now enable/disable the shortcut buttons
935  ui->tool_browse_app->setVisible(false);
936  ui->tool_browse_cat->setVisible(false);
937  ui->tool_browse_gotocat->setVisible(true);
938  //Setup the icon/name for the category display
939    QStringList catinfo = PBI->CatInfo(cat,QStringList() << "name" << "icon");
940    ui->tool_browse_cat->setText(catinfo[0]);
941    ui->label_cat_name->setText(catinfo[0]);
942    if(catinfo[1].isEmpty()){ catinfo[1] = defaultIcon; }
943    ui->tool_browse_cat->setIcon(QIcon(catinfo[1]));
944    ui->label_cat_icon->setPixmap( QPixmap(catinfo[1]).scaled(32,32) ); 
945  //Now move to the page
946  ui->tabWidget->setCurrentWidget(ui->tab_browse);
947  ui->stacked_browser->setCurrentWidget(ui->page_cat);
948  //Now save that this category is currently displayed
949  cCat = cat;
950}
951
952void MainUI::slotGoToApp(QString appID){
953  qDebug() << "Show App:" << appID;
954  //Get the general application info
955  QStringList data; 
956  data << "name" << "icon" << "author" << "website" << "license" << "type" << "description" \
957       << "category" << "requiresroot" << "latestversion" << "latestarch" <<"latestsize" \
958       << "backupversion" << "backuparch" << "backupsize";
959  data = PBI->AppInfo(appID,data);
960  if(data.isEmpty()){
961    qDebug() << "Invalid App:" << appID;
962    return;
963  }
964  cApp = appID; //save this for later
965  //Start the search for similar apps
966  PBI->searchSimilar = appID;
967  ui->group_bapp_similar->setVisible(FALSE);
968  QTimer::singleShot(0,PBI,SLOT(startSimilarSearch()));
969  //Now Check icon
970  if(data[1].isEmpty()){ data[1] = defaultIcon; }
971  //Now fill the UI with the data
972  ui->label_bapp_name->setText(data[0]);
973  ui->label_bapp_icon->setPixmap(QPixmap(data[1]));
974  if(data[3].isEmpty()){ ui->label_bapp_authorweb->setText(data[2]); }
975  else{ ui->label_bapp_authorweb->setText("<a href="+data[3]+">"+data[2]+"</a>"); }
976  ui->label_bapp_authorweb->setToolTip(data[3]);
977  ui->label_bapp_license->setText(data[4]);
978  ui->label_bapp_type->setText(data[5]);
979  ui->text_bapp_description->setText(data[6]);
980  //Now determine the appropriate version info
981  QString pbiID = PBI->isInstalled(appID); //get pbiID
982  QString cVer;
983  if(!pbiID.isEmpty()){ cVer = PBI->PBIInfo(pbiID,QStringList("version")).join(""); }
984  bool useLatest=FALSE;
985  bool nobackup = data[12].isEmpty();
986  if(cVer.isEmpty()){ useLatest=TRUE; } //not currently installed
987  else if(cVer != data[9]){ useLatest=TRUE;} //not the latest version
988  //Now put the proper version info on the UI
989  if(useLatest || nobackup){
990    ui->label_bapp_version->setText(data[9]);
991    ui->label_bapp_arch->setText(data[10]);
992    if(data[11].isEmpty()){ ui->label_bapp_size->setText(tr("Unknown")); }
993    else{ ui->label_bapp_size->setText( Extras::sizeKToDisplay(data[11]) ); }
994  }else{
995    ui->label_bapp_version->setText(data[12]);
996    ui->label_bapp_arch->setText(data[13]);
997    if(data[14].isEmpty()){ ui->label_bapp_size->setText(tr("Unknown")); }
998    else{ ui->label_bapp_size->setText( Extras::sizeKToDisplay(data[14]) ); }
999  }
1000  //Now update the download button appropriately
1001  slotUpdateAppDownloadButton();
1002
1003  //Now enable/disable the shortcut buttons
1004  ui->tool_browse_app->setVisible(TRUE);
1005    ui->tool_browse_app->setText(data[0]);
1006    ui->tool_browse_app->setIcon(QIcon(data[1]));
1007  QStringList catinfo = PBI->CatInfo(Extras::nameToID(data[7]),QStringList() << "name" << "icon");
1008  if(!catinfo.isEmpty()){
1009    ui->tool_browse_gotocat->setVisible(false);
1010    ui->tool_browse_cat->setVisible(TRUE);
1011    ui->tool_browse_cat->setText(catinfo[0]);
1012    if(catinfo[1].isEmpty()){ catinfo[1] = defaultIcon; }
1013    ui->tool_browse_cat->setIcon(QIcon(catinfo[1]));
1014  }
1015  ui->tabWidget->setCurrentWidget(ui->tab_browse);
1016  ui->stacked_browser->setCurrentWidget(ui->page_app);
1017       
1018}
1019
1020void MainUI::slotUpdateAppDownloadButton(){
1021  QString ico;
1022  QString working = PBI->currentAppStatus(cApp);
1023  QString rawstat = PBI->currentAppStatus(cApp, true);
1024  QStringList info = PBI->AppInfo(cApp, QStringList() << "latestversion" << "backupversion" << "requiresroot");
1025  QString pbiID = PBI->isInstalled(cApp);
1026  //qDebug() << "App Download status:" << working << rawstat;
1027  if(!working.isEmpty() && !rawstat.isEmpty() ){ //app currently pending or actually doing something
1028    if(rawstat.startsWith("DLSTAT::")){ ui->tool_bapp_download->setText(tr("Downloading..")); }
1029    else{ ui->tool_bapp_download->setText(working); }
1030    ui->tool_bapp_download->setIcon(QIcon(":icons/working.png"));
1031    ui->tool_bapp_download->setEnabled(FALSE);
1032  }else if( pbiID.isEmpty() ){ //new installation
1033    ui->tool_bapp_download->setText(tr("Install Now!"));
1034    ico = ":icons/app_download.png";
1035    ui->tool_bapp_download->setEnabled(TRUE);
1036  }else if( !PBI->upgradeAvailable(pbiID).isEmpty() ){ //Update available
1037    ui->tool_bapp_download->setText(tr("Update"));
1038    ico = ":icons/app_upgrade.png";
1039    ui->tool_bapp_download->setEnabled(TRUE);
1040  }else if(!info[1].isEmpty()){  //Downgrade available
1041    ui->tool_bapp_download->setText(tr("Downgrade"));
1042    ico = ":icons/app_downgrade.png";
1043    ui->tool_bapp_download->setEnabled(TRUE);
1044  }else{ //already installed (no downgrade available)
1045    ui->tool_bapp_download->setText(tr("Installed"));
1046    ui->tool_bapp_download->setIcon(QIcon(":icons/dialog-ok.png"));
1047    ui->tool_bapp_download->setEnabled(FALSE);
1048  }
1049  //Now set the icon appropriately if it requires root permissions
1050  if(!ico.isEmpty()){
1051    if(info[2]=="true"){ //requires root permissions to install
1052      ico.replace(".png","-root.png");
1053    }
1054    ui->tool_bapp_download->setIcon(QIcon(ico));
1055  }
1056  ui->tool_bapp_download->setWhatsThis(cApp); //set for slot
1057}
1058
1059void MainUI::slotGoToSearch(){
1060  searchTimer->stop(); //just in case "return" was pressed to start the search
1061  QString search = ui->line_browse_searchbar->text();
1062  if(search.isEmpty()){ return; }
1063  PBI->searchTerm = search;
1064  QTimer::singleShot(1,PBI,SLOT(startAppSearch()));
1065  ui->label_bsearch_info->setText( tr("Searching the application database. Please Wait....") );
1066    ui->label_bsearch_info->setVisible(TRUE);
1067    ui->group_bsearch_best->setVisible(FALSE);
1068    ui->group_bsearch_other->setVisible(FALSE);
1069 
1070}
1071       
1072void MainUI::slotShowSimilarApps(QStringList apps){
1073  qDebug() << " - Similar applications:" << apps;
1074  if(apps.isEmpty()){ ui->group_bapp_similar->setVisible(FALSE); }
1075  else{
1076    clearScrollArea(ui->scroll_bapp_similar);
1077    QHBoxLayout *layout = new QHBoxLayout;
1078    for(int i=0; i<apps.length(); i++){
1079      QStringList appdata = PBI->AppInfo(apps[i],QStringList() << "name" << "icon");
1080      if(!appdata.isEmpty()){
1081        SmallItemWidget *item = new SmallItemWidget(apps[i],appdata[0],appdata[1],"");
1082        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1083        layout->addWidget(item);
1084      }
1085    }
1086    layout->addStretch(); //add a spacer to the end
1087    layout->setContentsMargins(1,1,1,1);
1088    ui->scroll_bapp_similar->widget()->setLayout(layout);
1089    //Make sure that the similar scrollarea is the proper fit vertically (no vertical scrolling)
1090    ui->scroll_bapp_similar->setMinimumHeight(ui->scroll_bapp_similar->widget()->minimumSizeHint().height() +ui->scroll_bapp_similar->horizontalScrollBar()->height()/1.2);
1091    //Now make the group visible as appropriate
1092    ui->group_bapp_similar->setVisible(TRUE);
1093    if(ui->group_bapp_similar->isChecked()){ ui->scroll_bapp_similar->setVisible(TRUE); }
1094    else{ ui->scroll_bapp_similar->setVisible(FALSE); }
1095    //Now make sure the app page has the proper layout dimensions
1096    ui->page_app->updateGeometry();
1097  }
1098}
1099
1100void MainUI::slotShowSearchResults(QStringList best, QStringList rest){
1101  //Now display the search results
1102  if(best.isEmpty()){
1103    ui->label_bsearch_info->setText( QString(tr("No Search Results Found for the term: %1")).arg(ui->line_browse_searchbar->text()) );
1104    ui->label_bsearch_info->setVisible(TRUE);
1105    ui->group_bsearch_best->setVisible(FALSE);
1106    ui->group_bsearch_other->setVisible(FALSE);
1107  }else{
1108    ui->label_bsearch_info->setVisible(FALSE);
1109    //Now fill the "best" section
1110    clearScrollArea(ui->scroll_bsearch_best);
1111    QVBoxLayout *layout = new QVBoxLayout;
1112    QStringList info; info << "name" << "shortdescription" << "icon" << "type";
1113    for(int i=0; i<best.length(); i++){
1114      QStringList data = PBI->AppInfo(best[i],info);
1115      if(!data.isEmpty()){
1116        //Trim the description if necessary
1117        if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
1118        LargeItemWidget *item = new LargeItemWidget(best[i],data[0],data[1],data[2]);
1119        item->setType(data[3].toLower());
1120        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1121        layout->addWidget(item); 
1122      }
1123    }
1124    layout->addStretch();
1125    ui->scroll_bsearch_best->widget()->setLayout(layout);
1126    ui->group_bsearch_best->setVisible(TRUE);
1127   
1128    //Now fill the other results
1129    if(rest.isEmpty()){ ui->group_bsearch_other->setVisible(FALSE); }
1130    else{
1131      ui->group_bsearch_other->setVisible(TRUE);
1132      clearScrollArea(ui->scroll_bsearch_other);
1133      QVBoxLayout *layout2 = new QVBoxLayout;
1134      for(int i=0; i<rest.length(); i++){
1135        QStringList data = PBI->AppInfo(rest[i],info);
1136        if(!data.isEmpty()){
1137          //Trim the description if necessary
1138          if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
1139          LargeItemWidget *item = new LargeItemWidget(rest[i],data[0],data[1],data[2]);
1140          item->setType(data[3].toLower());
1141          connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1142          layout2->addWidget(item); 
1143        }
1144      }
1145      layout2->addStretch();
1146      ui->scroll_bsearch_other->widget()->setLayout(layout2);
1147    }
1148  }
1149       
1150  //need to make sure the search bar still has keyboard focus (just in case)
1151  ui->tabWidget->setCurrentWidget(ui->tab_browse);
1152  ui->stacked_browser->setCurrentWidget(ui->page_search);
1153 
1154}
1155
1156void MainUI::on_tabWidget_currentChanged(){
1157  if(ui->tabWidget->currentWidget() == ui->tab_browse){
1158    //Refresh the app page if that is the one currently showing
1159    if(ui->stacked_browser->currentWidget() == ui->page_app){ on_tool_browse_app_clicked(); }     
1160  }else{
1161    //Always return to the installed list
1162    ui->stackedWidget->setCurrentWidget(ui->page_install_list);
1163  }
1164}
1165
1166void MainUI::on_tool_browse_home_clicked(){
1167  slotGoToHome();
1168}
1169
1170void MainUI::on_tool_browse_cat_clicked(){
1171  QString cat = Extras::nameToID(ui->tool_browse_cat->text());
1172  if(cCat == cat){ //already loaded - just show it (prevents resetting scroll positions)
1173    ui->stacked_browser->setCurrentWidget(ui->page_cat);
1174    //Now enable/disable the shortcut buttons
1175    ui->tool_browse_app->setVisible(false);
1176    ui->tool_browse_cat->setVisible(false);
1177    ui->tool_browse_gotocat->setVisible(true);
1178  }else{ //load and show the category
1179    slotGoToCategory(cat);
1180  }
1181}
1182
1183void MainUI::on_tool_browse_app_clicked(){
1184  QString appID = Extras::nameToID(ui->tool_browse_app->text());
1185  slotGoToApp(appID);
1186}
1187
1188void MainUI::on_line_browse_searchbar_textChanged(){
1189  //Connect this to a singleshot timer, so the search functionality is only
1190  //  run once after a short wait rather than every time a new character is typed
1191 
1192  //Live search only after 3 characters have been typed
1193  if(ui->line_browse_searchbar->text().length() > 2){
1194    searchTimer->start();
1195  }else{
1196    searchTimer->stop();         
1197  }
1198}
1199
1200void MainUI::on_tool_bapp_download_clicked(){
1201  QString appID = ui->tool_bapp_download->whatsThis();
1202  //Verify the app installation
1203  if( QMessageBox::Yes != QMessageBox::question(this,tr("Verify Installation"), tr("Are you sure you want to install this application?")+"\n\n"+appID,QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)){
1204    return;
1205  }
1206  PBI->installApp(QStringList() << appID);
1207  ui->tool_bapp_download->setEnabled(FALSE); //make sure it cannot be clicked more than once before page refresh
1208  //Now show the Installed tab
1209  ui->tabWidget->setCurrentWidget(ui->tab_installed);
1210}
1211
1212void MainUI::on_group_br_home_newapps_toggled(bool show){
1213  ui->scroll_br_home_newapps->setVisible(show);
1214}
1215
1216void MainUI::on_group_bapp_similar_toggled(bool show){
1217  ui->scroll_bapp_similar->setVisible(show);
1218}
1219
1220// ================================
1221// ==========  OTHER ==============
1222// ================================
1223void MainUI::clearScrollArea(QScrollArea* area){
1224  QWidget *wgt = area->takeWidget();
1225  delete wgt; //delete the widget and all children
1226  area->setWidget( new QWidget() ); //create a new widget in the scroll area
1227  area->widget()->setContentsMargins(0,0,0,0);
1228}
1229
1230void MainUI::slotDisplayError(QString title,QString message,QStringList log){
1231  QMessageBox *dlg = new QMessageBox(this);
1232    dlg->setWindowTitle(title);
1233    dlg->setText(message);
1234    dlg->setDetailedText(log.join("\n"));
1235  dlg->exec();
1236}
1237
1238void MainUI::slotDisplayStats(){
1239  int avail = PBI->numAvailable;
1240  int installed = PBI->numInstalled;
1241  QString text;
1242  if(avail == -1){
1243    text = QString(tr("Installed: %1")).arg(QString::number(installed)); 
1244  }else{
1245    text = QString(tr("Installed: %1")+"\t"+tr("Available: %2")).arg(QString::number(installed), QString::number(avail)); 
1246  }
1247  //Get the number of installed/available applications and display it
1248  statusLabel->setText(text);   
1249}
Note: See TracBrowser for help on using the repository browser.