//OpenSCADA module UI.QTStarter file: tuimod.cpp /*************************************************************************** * Copyright (C) 2005-2024 by Roman Savochenko, * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; version 2 of the License. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tuimod.h" #if QT_VERSION < 0x060000 # include # define position() pos() # define globalPosition() globalPos() # define typeId() type() #endif //************************************************* //* Modul info! * #define MOD_ID "QTStarter" #define MOD_NAME trS("Qt GUI starter") #define MOD_TYPE SUI_ID #define VER_TYPE SUI_VER #define MOD_VER "6.0.7" #define AUTHORS trS("Roman Savochenko") #define DESCRIPTION trS("Provides the Qt GUI starter. Qt-starter is the only and compulsory component for all GUI modules based on the Qt library.") #define LICENSE "GPL2" //************************************************* QTStarter::TUIMod *QTStarter::mod; extern "C" { #ifdef MOD_INCL TModule::SAt ui_QTStarter_module( int n_mod ) #else TModule::SAt module( int n_mod ) #endif { if(n_mod == 0) return TModule::SAt(MOD_ID, MOD_TYPE, VER_TYPE); return TModule::SAt(""); } #ifdef MOD_INCL TModule *ui_QTStarter_attach( const TModule::SAt &AtMod, const string &source ) #else TModule *attach( const TModule::SAt &AtMod, const string &source ) #endif { if(AtMod == TModule::SAt(MOD_ID,MOD_TYPE,VER_TYPE)) return new QTStarter::TUIMod(source); return NULL; } } using namespace QTStarter; //************************************************* //* TUIMod * //************************************************* TUIMod::TUIMod( string name ) : TUI(MOD_ID), mQtLookMdf(false), QtApp(NULL), hideMode(false), mEndRun(false), mStartCom(false), mCloseToTray(DEF_CloseToTray), mSessCntr(DEF_SessCntr), mStartMod(dataRes()), mStyle(dataRes()), mFont(dataRes()), mPalette(dataRes()), mStyleSheets(dataRes()), qtArgC(0), qtArgEnd(0), splashTp(SPLSH_NULL), splash(NULL), splashTm(0) { mod = this; modInfoMainSet(MOD_NAME, MOD_TYPE, MOD_VER, AUTHORS, DESCRIPTION, LICENSE, name); //Qt massages not for compile but for indexing by gettext #if 0 char mess[][100] = { _("Could not read image data"), _("&Yes"),_("&No"),_("&Cancel"),_("&OK"),_("Apply"),_("Close"),_("Back"),_("Forward"),_("Parent Directory"), _("Look in:"),_("Computer"),_("File"),_("Folder"),_("File &name:"),_("Open"),_("&Open"),_("Cancel"),_("Save"),_("&Save"),_("Save As"),_("Date Modified"),_("All Files (*)"), _("Create New Folder"),_("List View"),_("Detail View"),_("Files of type:"),_("New Folder"),_("&New Folder"),_("Show &hidden files"),_("&Delete"),_("&Rename"),_("Remove"), _("&Undo"),_("&Redo"),_("Cu&t"),_("&Copy"),_("&Paste"),_("Delete"),_("Select All"),_("Insert Unicode control character"), _("Size"),_("Type"),_("Drive"),_("Go back"),_("Go forward"),_("Reload"),_("Save page"),_("View page source"), _("Go to the parent directory"),_("Create a New Folder"),_("Change to list view mode"),_("Change to detail view mode"),_("Destination file exists"), _("%1 bytes"),_("%1 KB"),_("%1 MB"), _("Are sure you want to delete '%1'?"),_("%1 already exists.\nDo you want to replace it?"),_("Recent Places"), _("

About Qt

This program uses Qt version %1.

"), _("

Qt is a C++ toolkit for cross-platform application development.

Qt provides single-source portability across MS Windows, Mac OS X, Linux, and all major commercial Unix variants. Qt is also available for embedded devices as Qt for Embedded Linux and Qt for Windows CE.

Qt is available under three different licensing options designed to accommodate the needs of our various users.

Qt licensed under our commercial license agreement is appropriate for development of proprietary/commercial software where you do not want to share any source code with third parties or otherwise cannot comply with the terms of the GNU LGPL version 2.1 or GNU GPL version 3.0.

Qt licensed under the GNU LGPL version 2.1 is appropriate for the development of Qt applications (proprietary or open source) provided you can comply with the terms and conditions of the GNU LGPL version 2.1.

Qt licensed under the GNU General Public License version 3.0 is appropriate for the development of Qt applications where you wish to use such applications in combination with software subject to the terms of the GNU GPL version 3.0 or where you are otherwise willing to comply with the terms of the GNU GPL version 3.0.

Please see qt.digia.com/product/licensing for an overview of Qt licensing.

Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).

Qt is a Digia product. See qt.digia.com for more information.

"), _("Hu&e:"),_("&Sat:"),_("&Val:"),_("&Red:"),_("&Green:"),_("Bl&ue:"),_("A&lpha channel:"),_("&Basic colors"),_("&Custom colors"),_("&Add to Custom Colors"),_("Select color"), _("&Restore"),_("&Move"),_("&Size"),_("Mi&nimize"),_("Ma&ximize"),_("Stay on &Top"),_("&Close"),_("Select Color"), _("Form"),_("Printer"),_("&Name:"),_("P&roperties"),_("Location:"),_("Preview"),_("Type:"),_("Output &file:"),_("Print range"),_("Print all"),_("Current Page"), _("Pages from"),_("to"),_("Selection"),_("Output Settings"),_("Copies:"),_("Collate"),_("Reverse"),_("Copies"),_("Color Mode"),_("Color"),_("Grayscale"), _("Duplex Printing"),_("None"),_("Long side"),_("Short side"),_("Options"),_("&Options >>"),_("&Options <<"),_("&Print"),_("Print"),_("Print to File (PDF)"),_("Print to File (Postscript)"), _("Local file"),_("Write %1 file"),_("Paper"),_("Page size:"),_("Width:"),_("Height:"),_("Paper source:"),_("Orientation"),_("Portrait"),_("Landscape"),_("Reverse landscape"), _("Reverse portrait"),_("Margins"),_("top margin"),_("left margin"),_("right margin"),_("bottom margin"),_("Points (pt)"),_("Inches (in)"), _("Executive"),_("Folio"),_("Ledger"),_("Legal"),_("Letter"),_("Tabloid"),_("US Common #10 Envelope"),_("Custom"), _("Millimeters (mm)"),_("Centimeters (cm)"),_("Page"),_("Advanced"), _("Mon"), }; #endif //Look and feel DB structure elLF.fldAdd(new TFld("NAME","",TFld::String,TCfg::Key,i2s(limObjNm_SZ).c_str())); elLF.fldAdd(new TFld("STYLE","",TFld::String,0,"20")); elLF.fldAdd(new TFld("FONT","",TFld::String,0,"30")); elLF.fldAdd(new TFld("PALETTE","",TFld::String,0,"1000")); elLF.fldAdd(new TFld("STL_SHTS","",TFld::String,0,"100000")); } TUIMod::~TUIMod( ) { if(runSt) modStop(); } string TUIMod::style( bool mant ) { return mant ? mStyle.getVal() : (mStyle.getVal().size() ? mStyle.getVal() : SYS->cmdOpt("style")); } void TUIMod::postEnable( int flag ) { TModule::postEnable(flag); if(!(flag&TCntrNode::NodeConnect)) return; //Set up Qt environments qtArgC = qtArgEnd = 0; toQtArg(SYS->cmdOpt("").c_str()); #if QT_VERSION < 0x050000 QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale()); //codepage for Qt across QString recode! #endif //Check command line for options no help and no daemon hideMode = SYS->cmdOptPresent("h") || SYS->cmdOptPresent("help") || SYS->cmdOptPresent("demon") || SYS->cmdOptPresent("daemon") || SYS->cmdOptPresent("nox11"); string argCom, argVl; for(int off = 0; (argCom=TSYS::strParse("sync:widgetcount:qws:style:stylesheet:session:reverse:graphicssystem:display:geometry",0,":",&off)).size(); ) if((argVl=SYS->cmdOpt(argCom)).size()) toQtArg(argCom.c_str(), argVl.c_str()); if(SYS->cmdOpt("showWin").empty()) { #if defined(Q_WS_MAEMO) || defined(__ANDROID__) SYS->cmdOpt("showWin", "1"); //To maximize #endif } if(!SYS->cmdOptPresent("QtInNotMainThread")) { if(SYS->mainThr.freeStat()) SYS->mainThr = this; if(hideMode) return; QLocale::setDefault(QLocale(Mess->lang().c_str())); #if HAVE_WEBENGINE QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); #endif //Qt application object init QtApp = new StApp(mod->qtArgC, (char**)&mod->qtArgV); splashSet(SPLSH_START); } //Start main Qt thread if not help and not daemon else if(!(runSt || hideMode)) { mEndRun = false; SYS->taskCreate(nodePath('.',true), 0, Task, this); } } void TUIMod::preDisable( int flag ) { if(SYS->stopSignal() == SIGUSR2) throw err_sys(_("Hold when overloaded to another project.")); } void TUIMod::postDisable( int flag ) { if(!SYS->cmdOptPresent("QtInNotMainThread")) { if(hideMode) return; splashSet(SPLSH_NULL); //Qt application object free if(QtApp) delete QtApp; } else if(runSt) SYS->taskDestroy(nodePath('.',true), &mEndRun, 10, true); } void TUIMod::perSYSCall( unsigned int cnt ) { if(hideMode || runSt || !splash || splashTp == SPLSH_NULL) return; splashSet((cnt==SPLSH_START||cnt==SPLSH_STOP)?(TUIMod::SplashFlag)cnt:splashTp); } void TUIMod::load_( ) { mess_debug(nodePath().c_str(),_("Loading the module.")); //Load parameters from command line //Load parameters from config-file setStartMod(TBDS::genPrmGet(nodePath()+"StartMod")); setCloseToTray(s2i(TBDS::genPrmGet(nodePath()+"CloseToTray",i2s(DEF_CloseToTray)))); sessCntr(true); setStyle(TBDS::genPrmGet(nodePath()+"Style")); setFont(TBDS::genPrmGet(nodePath()+"Font")); setPalette(TBDS::genPrmGet(nodePath()+"Palette")); setStyleSheets(TBDS::genPrmGet(nodePath()+"StyleSheets")); } void TUIMod::save_( ) { mess_debug(nodePath().c_str(),_("Saving the module.")); TBDS::genPrmSet(nodePath()+"StartMod", startMod()); TBDS::genPrmSet(nodePath()+"CloseToTray", i2s(closeToTray())); TBDS::genPrmSet(nodePath()+"SessCntr", i2s(sessCntr()), "root", TBDS::OnlyCfg); TBDS::genPrmSet(nodePath()+"Style", style()); TBDS::genPrmSet(nodePath()+"Font", font()); TBDS::genPrmSet(nodePath()+"Palette", palette()); TBDS::genPrmSet(nodePath()+"StyleSheets", styleSheets()); sess(SYS->prjNm()); // SessCntr acceptation in the session file } int TUIMod::sessCntr( bool reload ) { if(reload) mSessCntr = s2i(TBDS::genPrmGet(nodePath()+"SessCntr",i2s(DEF_SessCntr))); return mSessCntr; } void TUIMod::modStart( ) { if(!SYS->cmdOptPresent("QtInNotMainThread")) { if(!SYS->isRunning() || runSt || hideMode) return; mess_debug(nodePath().c_str(), _("Starting the module.")); if(splash && splashTp != SPLSH_START) splashSet(SPLSH_START); runSt = true; //Start external modules QtApp->stExec(); splashSet(SPLSH_STOP); runSt = false; } else { mess_debug(nodePath().c_str(), _("Starting the module.")); mStartCom = true; } } void TUIMod::modStop( ) { if(SYS->cmdOptPresent("QtInNotMainThread")) { mess_debug(nodePath().c_str(), _("Stopping the module.")); mStartCom = false; } } void TUIMod::splashSet( SplashFlag flg ) { MtxAlloc res(splashRes, true); if(flg == SPLSH_NULL) { if(splash) delete splash; splash = NULL; } else { QFont wFnt; //Obtaining the splash image QImage ico_t; string ico_f; bool sysSplash = false; if((ico_f=TUIS::icoGet(SYS->id()+((flg==SPLSH_STOP)?"_splash_exit":"_splash"),NULL,true)).empty() || !ico_t.load(ico_f.c_str())) { ico_t.load(":/images/splash.png"); sysSplash = true; } //Creating the splash if(splash && flg != splashTp) splashSet(SPLSH_NULL); if(!splash) { splash = new QSplashScreen(); splashTm = time(NULL); //Prepairing the splash image QPixmap pm = QPixmap::fromImage(ico_t); QPainter pnt(&pm); QPen pen(Qt::DotLine); pen.setColor("gray"); pen.setWidth(1); pnt.setPen(pen); QColor barColor("ivory"); barColor.setAlpha(170); // Appending the project ico string icoImg = TSYS::strDecode(SYS->ico(), TSYS::base64); if(sysSplash && icoImg.size()) { QImage prjPm; prjPm.loadFromData((const uchar*)icoImg.data(), icoImg.size()); prjPm = prjPm.scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation); // In plain rect //pnt.drawRect(pm.width()-prjPm.width(), 0, prjPm.width(), prjPm.height()); //pnt.fillRect(pm.width()-prjPm.width(), 0, prjPm.width(), prjPm.height(), barColor); //pnt.drawImage(pm.width()-prjPm.width(), 0, prjPm); // In gradient circle QRect icoArea(pm.width()-68, 0, 68, 68); QPainterPath path; QRadialGradient grd(icoArea.center(), icoArea.width()/2, icoArea.center()-QPoint(10,10)); grd.setColorAt(0, "white"); grd.setColorAt(1, "gray"); pnt.setBrush(grd); path.moveTo(pm.width()-icoArea.width()/2, icoArea.height()/2); path.arcTo(icoArea, 0, 360); pnt.drawPath(path); pnt.drawImage(icoArea.adjusted(10,10,-10,-10), prjPm); } // Appending the program and the project information pnt.setBrush(barColor); QRect infBar(0, pm.height()-18, pm.width(), 18); pnt.fillRect(infBar, barColor); pnt.drawRect(infBar); wFnt = splash->font(); wFnt.setPixelSize(15); pnt.setFont(wFnt); pnt.drawText(infBar.adjusted(4,1,-4,-1), Qt::AlignRight, SYS->prjNm().c_str()); pnt.drawText(infBar.adjusted(4,1,-4,-1), sysSplash ? PACKAGE_VERSION : PACKAGE_STRING); //Starting the splash splash->setPixmap(pm); splash->show(); } wFnt = splash->font(); wFnt.setPixelSize(10); splash->setFont(wFnt); //Updating messages vector recs; SYS->archive().at().messGet(splashTm/*time(NULL)-120*/, time(NULL), recs, "", TMess::Debug, ARCH_BUF); QString mess; for(int iM = recs.size()-1; iM >= 0 && iM > ((int)recs.size()-10); iM--) mess += QString("%1\n").arg(recs[iM].mess.c_str()); recs.clear(); splash->showMessage(mess, Qt::AlignBottom|Qt::AlignLeft); for(int iTr = 0; iTr < 3; iTr++) { QtApp->processEvents(); //!!!! To show the messages on Qt5 TSYS::sysSleep(0.01); //!!!! To ensure the splash visibility at the exit on Qt5 } } splashTp = flg; } string TUIMod::sess( const string &proj, int *md ) { if(!QtApp) return ""; string rezPrj, s_buf, s_file = oscd_datadir_full "/gui.sess"; //Reading the sessions file int hd = open(s_file.c_str(), O_RDONLY); if(hd >= 0) { int cf_sz = lseek(hd, 0, SEEK_END); if(cf_sz > 0) { lseek(hd, 0, SEEK_SET); char buf[prmStrBuf_SZ]; for(int len = 0; (len=read(hd,buf,sizeof(buf))) > 0; ) s_buf.append(buf, len); } if(close(hd) != 0) mess_warning(mod->nodePath().c_str(), _("Closing the file %d error '%s (%d)'!"), hd, strerror(errno), errno); } //Parsing XML XMLNode sess(PACKAGE_NAME); try{ sess.load(s_buf); } catch(TError&) { } //Searching the project for saving the current session if(proj.size() && (hd=open(s_file.c_str(),O_CREAT|O_TRUNC|O_WRONLY,SYS->permCrtFiles())) >= 0) { XMLNode *prjN = sess.getElementBy("id", proj); if(!prjN) prjN = sess.childAdd("project")->setAttr("id", proj); prjN->setAttr("sess", QtApp->sessionId().toStdString())->setAttr("md", i2s(mod->sessCntr(true))); // Removing the previous project record due to switch if(SYS->prjNm().size() && SYS->prjNm() != proj && (prjN=sess.getElementBy("id",SYS->prjNm()))) prjN->parent()->childDel(prjN); s_buf = sess.save(XMLNode::XMLHeader|XMLNode::BrAllPast); int rez = write(hd, s_buf.data(), s_buf.size()); if(rez != (int)s_buf.size()) mod->mess_sys(TMess::Error,_("Error writing the file '%s': %s"), s_file.c_str(), ((rez<0)?strerror(errno):"")); else rezPrj = proj; if(close(hd) != 0) mess_warning(mod->nodePath().c_str(), _("Closing the file %d error '%s (%d)'!"), hd, strerror(errno), errno); } //Searching the project at the session ID for restoring else { XMLNode *prjN = sess.getElementBy("sess", QtApp->sessionId().toStdString()); if(prjN) { rezPrj = prjN->attr("id"); if(md) *md = s2i(prjN->attr("md")); } } return rezPrj; } string TUIMod::optDescr( ) { return TSYS::strMess(_( "======================= Module <%s:%s> options =======================\n" " --QtInNotMainThread Starts Qt into a different from the main thread.\n" " --showWin=<0,1,2> Window display mode, initial and which is allowed to change from: 0-typical window, 1-maximized window, 2-full screen.\n" " --simulRightMKeyTm= Timeout, in seconds, to simulate the right mouse key and context menu at holding the left mouse key in this time - more to zero.\n" "----------- Qt debug commandline options ----------\n" " --noX11 Prevent the launch of Qt, preferably for a clean console.\n" " --sync Switch to Sync X11 for debugging.\n" " --widgetcount Print debug messages at output, the number of widgets\n" " left unselected and their maximum number.\n" "----------- Qt commandline options ----------------\n" " --qws Do this with Qt server software for embedded Linux.\n" " --style= Sets the GUI style to (windows, platinum, plastique, ...).\n" " --stylesheet= Set the style sheet from the file by .\n" " --session= Restore from the previous session with the specified .\n" " --reverse Set the layout in Qt::RightToLeft.\n" " --graphicssystem= Install rendering mechanism for screen widgets and QPixmaps (raster, opengl).\n" " --display= Set the X display (typically $DISPLAY).\n" " --geometry= Set the client geometry of the first display window.\n" "---- Parameters of the module section '%s' of the configuration file ----\n" "StartMod List of the modules that are started, separated ';'.\n" "CloseToTray <0|1> Closing all windows or starting without Qt modules to the system tray.\n" "SessCntr [0...*3] Sessions control-restart: 0-if running, 1-anyway, 2-immediately, 3-never.\n" "Style The GUI style of Qt.\n" "Font Common Qt font.\n" "Palette Twenty colors of the palette separated by symbol ',' in three lines for active, disabled and inactive groups.\n" "StyleSheets Rules of the Cascade Style Sheets.\n\n"), MOD_TYPE, MOD_ID, nodePath().c_str()); } void TUIMod::toQtArg( const char *nm, const char *arg ) { string plStr = nm; if(qtArgC) plStr.insert(0, "-"); //Name process if(qtArgC >= (int)(sizeof(qtArgV)/sizeof(char*)) || (qtArgEnd+plStr.size()+1) > sizeof(qtArgBuf)) return; strcpy(qtArgBuf+qtArgEnd, plStr.c_str()); qtArgV[qtArgC++] = qtArgBuf+qtArgEnd; qtArgEnd += plStr.size()+1; //Argument process if(arg) { plStr = arg; if(qtArgC >= (int)(sizeof(qtArgV)/sizeof(char*)) || (qtArgEnd+plStr.size()+1) > sizeof(qtArgBuf)) return; strcpy(qtArgBuf+qtArgEnd, plStr.c_str()); qtArgV[qtArgC++] = qtArgBuf+qtArgEnd; qtArgEnd += plStr.size()+1; } } void *TUIMod::Task( void * ) { QLocale::setDefault(QLocale(Mess->lang().c_str())); #if HAVE_WEBENGINE QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); #endif //Qt application object init mod->QtApp = new StApp(mod->qtArgC, (char**)&mod->qtArgV); mod->runSt = true; ret: string stProj = SYS->prjNm(), stProj_; while(!mod->startCom() && !mod->endRun()) { stProj_ = SYS->prjNm(); mod->splashSet((stProj == stProj_) ? SPLSH_START : SPLSH_NULL); stProj = stProj_; TSYS::sysSleep(0.1); } mod->QtApp->stExec(); if(SYS->stopSignal() == SIGUSR2) { mod->mStartCom = false; goto ret; } while(!mod->endRun()) { mod->splashSet(SPLSH_STOP); TSYS::sysSleep(0.1); } mod->splashSet(SPLSH_NULL); //Qt application object free delete mod->QtApp; mod->QtApp = NULL; mod->runSt = false; return NULL; } void TUIMod::cntrCmdProc( XMLNode *opt ) { //Get page info if(opt->name() == "info") { TUI::cntrCmdProc(opt); if(ctrMkNode("area",opt,-1,"/prm/cfg",_("Module options"))) { ctrMkNode("fld",opt,-1,"/prm/cfg/st_mod",_("Qt modules for startup"),RWRWR_,"root",SUI_ID,3, "tp","str", "dest","select", "select","/prm/cfg/lsQtMod"); ctrMkNode("fld",opt,-1,"/prm/cfg/closeToTray",_("Collapse and startup to the system tray"),RWRWR_,"root",SUI_ID,1,"tp","bool"); ctrMkNode("fld",opt,-1,"/prm/cfg/sessCntr",_("Sessions control-restart"),RWRWR_,"root",SUI_ID,5,"tp","dec","dest","select", "sel_id",TSYS::strMess("%d;%d;%d;%d",RestartIfRunning,RestartAnyway,RestartImmediately,RestartNever).c_str(), "sel_list",_("if running;anyway;immediately;never"), "help",_("Applied at the next start for the modes:\n" "- 'if running' - if it is still running when the session is shut down;\n" "- 'anyway' - wants to be started at the start of the next session;\n" "- 'immediately' - wants to be started immediately whenever it is not running;\n" "- 'never' - does not want to be restarted automatically.")); if(ctrMkNode("area",opt,-1,"/prm/LF",_("Look and feel"))) { ctrMkNode("fld",opt,-1,"/prm/LF/prfl",_("Known profiles"),RWRWR_,"root",SUI_ID,3,"tp","str","dest","select","select","/prm/LF/prflLs"); ctrMkNode("fld",opt,-1,"/prm/LF/stl",_("Widgets style"),RWRWR_,"root",SUI_ID,3,"tp","str","dest","sel_ed","select","/prm/LF/stlLs"); ctrMkNode("fld",opt,-1,"/prm/LF/font",_("Common font"),RWRWR_,"root",SUI_ID,1,"tp","str"); ctrMkNode("fld",opt,-1,"/prm/LF/plt",_("Palette"),RWRWR_,"root",SUI_ID,3,"tp","str","rows","4","SnthHgl","1"); ctrMkNode("fld",opt,-1,"/prm/LF/stlSheets",_("Style Sheets"),RWRWR_,"root",SUI_ID,3,"tp","str","rows","5","SnthHgl","1"); } } return; } //Process command to page string a_path = opt->attr("path"); if(a_path == "/prm/cfg/st_mod") { if(ctrChkNode(opt,"get",RWRWR_,"root",SUI_ID,SEC_RD)) opt->setText(startMod()); if(ctrChkNode(opt,"set",RWRWR_,"root",SUI_ID,SEC_WR)) setStartMod(opt->text()); } else if(a_path == "/prm/cfg/lsQtMod" && ctrChkNode(opt)) { string stMod = startMod(); vector list; mod->owner().modList(list); // Single-item directly for(unsigned iL = 0; stMod.size() && iL < list.size(); iL++) if(mod->owner().modAt(list[iL]).at().modInfo("SubType") == "Qt" && mod->owner().modAt(list[iL]).at().modFuncPresent("QMainWindow *openWindow();")) opt->childAdd("el")->setText(list[iL]); // Item combination for(unsigned iL = 0; iL < list.size(); iL++) if(mod->owner().modAt(list[iL]).at().modInfo("SubType") == "Qt" && mod->owner().modAt(list[iL]).at().modFuncPresent("QMainWindow *openWindow();") && !TRegExp("(^|;)"+list[iL]+"(;|$)","m").test(stMod)) opt->childAdd("el")->setText((stMod.size()?stMod+";":"")+list[iL]); opt->childAdd("el")->setText(""); } else if(a_path == "/prm/cfg/closeToTray") { if(ctrChkNode(opt,"get",RWRWR_,"root",SUI_ID,SEC_RD)) opt->setText(i2s(closeToTray())); if(ctrChkNode(opt,"set",RWRWR_,"root",SUI_ID,SEC_WR)) setCloseToTray(s2i(opt->text())); } else if(a_path == "/prm/cfg/sessCntr") { if(ctrChkNode(opt,"get",RWRWR_,"root",SUI_ID,SEC_RD)) opt->setText(i2s(sessCntr())); if(ctrChkNode(opt,"set",RWRWR_,"root",SUI_ID,SEC_WR)) setSessCntr(s2i(opt->text())); } else if(a_path == "/prm/LF/prfl") { if(ctrChkNode(opt,"get",RWRWR_,"root",SUI_ID,SEC_RD)) opt->setText(_("")) { TConfig cEl(&elLF); cEl.cfg("NAME").setS(opt->text()); if(TBDS::dataGet("",nodePath()+"LookFeel",cEl,TBDS::NoException)) { string tVl; if((tVl=cEl.cfg("STYLE").getS()).size()) if(style(true).empty()) setStyle(tVl); if((tVl=cEl.cfg("FONT").getS()).size()) if(font().empty()) setFont(tVl); if((tVl=cEl.cfg("PALETTE").getS()).size()) { string tPlt = palette(), tRez, tVl1; for(int iG = 0; iG < 3; iG++) { for(int iC = 0; iC < 20; iC++) tRez += ((tVl1=sTrm(TSYS::strParse(TSYS::strLine(tPlt,iG),iC,","))).size() ? tVl1 : sTrm(TSYS::strParse(TSYS::strLine(tVl,iG),iC,","))) + ((iC<19)?", ":""); tRez += (iG < 3) ? "\n" : ""; } setPalette(tRez); } if((tVl=cEl.cfg("STL_SHTS").getS()).size()) { if(!styleSheets().size()) setStyleSheets(tVl); else if(styleSheets().find(tVl) == string::npos) setStyleSheets(styleSheets()+"\n"+tVl); } } } } } else if(a_path == "/prm/LF/prflLs" && ctrChkNode(opt)) { opt->childAdd("el")->setText(_("")); opt->childAdd("el")->setText(_("")); TConfig cEl(&elLF); for(int fld_cnt = 0; TBDS::dataSeek("",nodePath()+"LookFeel",fld_cnt++,cEl); ) opt->childAdd("el")->setText(cEl.cfg("NAME").getS()); } else if(a_path == "/prm/LF/stl") { if(ctrChkNode(opt,"get",RWRWR_,"root",SUI_ID,SEC_RD)) opt->setText(style()); if(ctrChkNode(opt,"set",RWRWR_,"root",SUI_ID,SEC_WR)) setStyle(opt->text()); } else if(a_path == "/prm/LF/font") { if(ctrChkNode(opt,"get",RWRWR_,"root",SUI_ID,SEC_RD)) opt->setText(font()); if(ctrChkNode(opt,"set",RWRWR_,"root",SUI_ID,SEC_WR)) setFont(opt->text()); } else if(a_path == "/prm/LF/stlLs" && ctrChkNode(opt)) { QStringList sls = QStyleFactory::keys(); for(int iL = 0; iL < sls.size(); iL++) opt->childAdd("el")->setText(sls[iL].toStdString()); } else if(a_path == "/prm/LF/plt") { if(ctrChkNode(opt,"get",RWRWR_,"root",SUI_ID,SEC_RD)) opt->setText(palette()); if(ctrChkNode(opt,"set",RWRWR_,"root",SUI_ID,SEC_WR)) setPalette(opt->text()); if(ctrChkNode(opt,"SnthHgl",RWRWR_,"root",SUI_ID,SEC_RD)) { opt->setAttr("font", SYS->ui().at().fontSnthHglCode()); opt->childAdd("rule")->setAttr("expr", "#[0-9a-fA-F]{6}")->setAttr("color", "blue"); } } else if(a_path == "/prm/LF/stlSheets") { if(ctrChkNode(opt,"get",RWRWR_,"root",SUI_ID,SEC_RD)) opt->setText(styleSheets()); if(ctrChkNode(opt,"set",RWRWR_,"root",SUI_ID,SEC_WR)) setStyleSheets(opt->text()); if(ctrChkNode(opt,"SnthHgl",RWRWR_,"root",SUI_ID,SEC_RD)) { opt->setAttr("font", SYS->ui().at().fontSnthHglCode()); opt->childAdd("blk")->setAttr("beg", "/\\*")->setAttr("end", "\\*/")->setAttr("color", "gray")->setAttr("font_italic", "1"); opt->childAdd("blk")->setAttr("beg", "\\{")->setAttr("end", "\\}")->setAttr("color", "#666666")-> childAdd("rule")->setAttr("expr", ":[^;]+")->setAttr("color", "blue"); opt->childAdd("rule")->setAttr("expr", "(\\.|#)\\w+\\s")->setAttr("color", "darkorange"); } } else TUI::cntrCmdProc(opt); } TVariant TUIMod::objFuncCall( const string &iid, vector &prms, const string &user_lang ) { // Array sensors() // Get all available sensors of the Qt mobility. if(iid == "sensors") { #ifdef HAVE_QTSENSORS if(!QtApp) return false; return QtApp->sensCnt; #endif return false; } return TCntrNode::objFuncCall(iid, prms, user_lang); } //************************************************* //* StApp * //************************************************* StApp::StApp( int &argv, char **args ) : QApplication(argv, args), origStl(mod->dataRes()), inExec(false), transl(NULL), trayMenu(NULL), tray(NULL), stDlg(NULL), initExec(false), simulRightMKeyTm(0), mouseBtPress(0), mouseBtRecv(NULL), mouseBtHoldPos(QPoint()), mouseBtHoldPosG(QPoint()) { setApplicationName(PACKAGE_STRING); setQuitOnLastWindowClosed(false); startTimer(1e3*prmWait_DL); connect(this, SIGNAL(saveStateRequest(QSessionManager&)), this, SLOT(saveSessState(QSessionManager&))); } StApp::~StApp( ) { stClear(true); } int StApp::topLevelWindows( ) { int winCnt = 0; for(int iW = 0; iW < topLevelWidgets().size(); iW++) if(qobject_cast(topLevelWidgets()[iW]) && topLevelWidgets()[iW]->isVisible()) winCnt++; return winCnt; } bool StApp::notify( QObject *receiver, QEvent *event ) { if(event && simulRightMKeyTm > 0) { if(event->type() == QEvent::MouseButtonPress && ((QMouseEvent*)event)->button() == Qt::LeftButton) { mouseBtRecv = receiver; mouseBtHoldPos = ((QMouseEvent*)event)->position(), mouseBtHoldPosG = ((QMouseEvent*)event)->globalPosition(); mouseBtPress = TSYS::curTime(); } if(mouseBtPress && ((event->type() == QEvent::MouseButtonRelease && ((QMouseEvent*)event)->button() == Qt::LeftButton) || (event->type() == QEvent::MouseMove && (((QMouseEvent*)event)->globalPosition()-mouseBtHoldPosG).toPoint().manhattanLength() > QFontMetrics(font()).height()) || (event->type() == QEvent::FocusOut && mouseBtRecv == receiver))) mouseBtPress = 0; } return QApplication::notify(receiver, event); } int StApp::stExec( ) { stClear(); QObject::connect(this, SIGNAL(lastWindowClosed()), this, SLOT(lastWinClose())); inExec = true; int rez = exec(); inExec = false; return rez; } void StApp::stClear( bool inDestr ) { QObject::disconnect(this, SIGNAL(lastWindowClosed()), this, SLOT(lastWinClose())); if(tray) { inDestr ? delete tray : tray->deleteLater(); tray = NULL; } if(trayMenu) { inDestr ? delete trayMenu : trayMenu->deleteLater(); trayMenu = NULL; } if(stDlg) { inDestr ? delete stDlg : stDlg->deleteLater(); stDlg = NULL; } if(transl) { removeTranslator(transl); delete transl; transl = NULL; } #ifdef HAVE_QTSENSORS for(int iS = 0; iS < sensors.size(); iS++) { sensors[iS]->stop(); sensors[iS]->deleteLater(); /*delete sensors[iS];*/ } sensors.clear(); #endif initExec = false; } void StApp::timerEvent( QTimerEvent *event ) { if(!inExec) return; if(!initExec) { initExec = true; //!!: Set to the begin but here can be a multiple entry from processEvents() manual call, observed on QTCfg. simulRightMKeyTm = s2i(SYS->cmdOpt("simulRightMKeyTm")); //Create I18N translator transl = new I18NTranslator(); installTranslator(transl); origStl = style()->objectName().toStdString(); updLookFeel(); //Disable the native menu bar #if defined(__ANDROID__) QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); #endif //setAutoSipEnabled(true); vector list; mod->owner().modList(list); blockSignals(true); //Start external modules int op_wnd = 0; if(SYS->prjCustMode() || SYS->prjNm().size()) for(unsigned iL = 0; iL < list.size(); iL++) if(mod->owner().modAt(list[iL]).at().modInfo("SubType") == "Qt" && mod->owner().modAt(list[iL]).at().modFuncPresent("QMainWindow *openWindow();")) { AutoHD QtMod = mod->owner().modAt(list[iL]); // Search module in the start list int iOff = 0; string sEl; while((sEl=TSYS::strSepParse(mod->startMod(),0,';',&iOff)).size() && sEl != list[iL]) ; if(mod->startMod().size() && (!sEl.empty() || !iOff) && callQtModule(list[iL])) op_wnd++; } //delete splash; mod->splashSet(TUIMod::SPLSH_NULL); blockSignals(false); //Start the call dialog or to system tray if(!op_wnd) { if(mod->closeToTray() && (SYS->prjNm().size() || SYS->prjCustMode())) createTray(); else if(sessPrjToRestore.size() && SYS->prjSwitch(sessPrjToRestore)) ; else startDialog(); if(sessPrjToRestore.size()) sessPrjToRestore = ""; } #ifdef HAVE_QTSENSORS for(int iS = 0; iS < QSensor::sensorTypes().size(); iS++) { sensors.push_back(new QSensor(QSensor::sensorTypes()[iS],this)); sensors.back()->start(); } #endif return; } if(mod->mQtLookMdf) updLookFeel(); if(mouseBtPress && 1e-6*(TSYS::curTime()-mouseBtPress) >= simulRightMKeyTm) { QMouseEvent evPress(QEvent::MouseButtonPress, mouseBtHoldPos.toPoint(), mouseBtHoldPosG.toPoint(), Qt::RightButton, Qt::NoButton, Qt::NoModifier); sendEvent(mouseBtRecv, &evPress); QMouseEvent evRels(QEvent::MouseButtonRelease, mouseBtHoldPos.toPoint(), mouseBtHoldPosG.toPoint(), Qt::RightButton, Qt::NoButton, Qt::NoModifier); sendEvent(mouseBtRecv, &evRels); if(dynamic_cast(mouseBtRecv)) { QContextMenuEvent evCtxMenu(QContextMenuEvent::Mouse, mouseBtHoldPos.toPoint(), mouseBtHoldPosG.toPoint()); sendEvent(mouseBtRecv, &evCtxMenu); } mouseBtPress = 0; } #ifdef HAVE_QTSENSORS if(sensCnt.type() != TVariant::Object) sensCnt = new TArrayObj(); for(int iS = 0; iS < sensors.size(); iS++) { QSensorReading *rd = sensors[iS]->reading(); if(!rd) continue; TVariant oS = sensCnt.getO().at().propGet(i2s(iS)); if(oS.type() != TVariant::Object) { sensCnt.getO().at().propSet(i2s(iS), new TArrayObj()); oS = sensCnt.getO().at().propGet(i2s(iS)); } oS.getO().at().propSet("type", sensors[iS]->type().data()); oS.getO().at().propSet("identifier", sensors[iS]->identifier().data()); oS.getO().at().propSet("description", sensors[iS]->description().toStdString()); oS.getO().at().propSet("active", sensors[iS]->isActive()); oS.getO().at().propSet("connected", sensors[iS]->isConnectedToBackend()); oS.getO().at().propSet("busy", sensors[iS]->isBusy()); oS.getO().at().propSet("error", sensors[iS]->error()); for(int iV = 0; iV < rd->valueCount(); iV++) { QVariant tvl = rd->value(iV); switch(tvl.typeId()) { case QVariant::Bool: oS.getO().at().propSet(i2s(iV), tvl.toBool()); break; case QVariant::Int: case QVariant::UInt: case QVariant::LongLong: case QVariant::ULongLong: oS.getO().at().propSet(i2s(iV), (int64_t)tvl.toLongLong()); break; case QVariant::Double: oS.getO().at().propSet(i2s(iV), tvl.toDouble()); break; default: oS.getO().at().propSet(i2s(iV), tvl.toString().toStdString()); } } } #endif if(!SYS->cmdOptPresent("QtInNotMainThread")) { if(!SYS->stopSignal()) return; } else if(!mod->endRun() && mod->startCom()) return; QWidgetList wl = topLevelWidgets(); for(int iW = 0; iW < wl.size(); iW++) wl[iW]->setProperty("forceClose", true); closeAllWindows(); if(mod->startStat() && !activeWindow()) quit(); } void StApp::createTray( ) { QImage ico_t; if(SYS->prjNm().size()) ico_t.load(TUIS::icoGet(SYS->prjNm(),NULL,true).c_str()); if(ico_t.isNull() && SYS->prjCustMode()) ico_t.load(TUIS::icoGet(SYS->id(),NULL,true).c_str()); if(ico_t.isNull()) ico_t.load(":/images/oscada_qt.png"); if(!tray) tray = new QSystemTrayIcon(QPixmap::fromImage(ico_t)); QObject::connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayAct(QSystemTrayIcon::ActivationReason))); tray->setToolTip(QString(_("OpenSCADA Project: %1")).arg((SYS->prjCustMode()?SYS->name():SYS->prjNm()).c_str())); if(!trayMenu) trayMenu = new QMenu(); else trayMenu->clear(); QAction *tAct = trayMenu->addAction(QIcon(":/images/oscada_qt.png"), "QTStarter"); trayMenu->addSeparator(); QObject::connect(tAct, SIGNAL(triggered()), this, SLOT(startDialog())); makeStarterMenu(trayMenu); trayMenu->addSeparator(); tAct = trayMenu->addAction(QIcon(":/images/exit.png"), _("Exit the program")); tAct->setObjectName("*exit*"); QObject::connect(tAct, SIGNAL(triggered()), this, SLOT(callQtModule())); tray->setContextMenu(trayMenu); tray->show(); } void StApp::callQtModule( ) { QObject *obj = (QObject *)sender(); if(obj->objectName() == "*exit*") SYS->stop(); else { try{ callQtModule(obj->objectName().toStdString()); } catch(TError &err) { } } } void StApp::updLookFeel( ) { QStyle *appStl = QStyleFactory::create(mod->style().c_str()); if(appStl) QApplication::setStyle(appStl); if(mod->font().size()) { QFont tf = font(); tf.fromString(mod->font().c_str()); setFont(tf); } if(mod->palette().size()) { QPalette plt = palette(); string cGrp, cRl, tVl; for(int off = 0, iGrp = 0; ((cGrp=TSYS::strLine(mod->palette(),0,&off)).size() || off < (int)mod->palette().size()) && iGrp < 4; iGrp++) for(int off1 = 0, iRl = 0; ((cRl=TSYS::strParse(cGrp,0,",",&off1)).size() || off1 < (int)cGrp.size()) && iRl < 20; iRl++) { if(!(tVl=sTrm(cRl)).size()) continue; plt.setColor((QPalette::ColorGroup)iGrp, (QPalette::ColorRole)iRl, tVl.c_str()); } setPalette(plt); } if(mod->styleSheets().size() || mod->mQtLookMdf) setStyleSheet(mod->styleSheets().c_str()); mod->mQtLookMdf = false; } void StApp::makeStarterMenu( QWidget *mn, const QString &lang ) { if(!mn) { QMainWindow *w = dynamic_cast(sender()); if(!w) return; for(unsigned iAct = 0; (int)iAct < w->menuBar()->actions().length() && !mn; ++iAct) if(w->menuBar()->actions()[iAct]->text() == "QTStarter") mn = w->menuBar()->actions()[iAct]->menu(); if(!mn) { mn = new QMenu("QTStarter"); w->menuBar()->addMenu((QMenu*)mn); } } vector list; mod->owner().modList(list); for(unsigned iL = 0; iL < list.size(); iL++) if(mod->owner().modAt(list[iL]).at().modInfo("SubType") == "Qt" && mod->owner().modAt(list[iL]).at().modFuncPresent("QMainWindow *openWindow();")) { AutoHD QtMod = mod->owner().modAt(list[iL]); //Search for need action QAction *act = NULL; for(unsigned iAct = 0; (int)iAct < mn->actions().length() && !act; ++iAct) if(mn->actions()[iAct]->objectName() == list[iL].c_str()) act = mn->actions()[iAct]; //Create new need action if(!act) { QIcon icon; if(mod->owner().modAt(list[iL]).at().modFuncPresent("QIcon icon();")) { QIcon(TModule::*iconGet)(); mod->owner().modAt(list[iL]).at().modFunc("QIcon icon();",(void (TModule::**)()) &iconGet); icon = ((&mod->owner().modAt(list[iL]).at())->*iconGet)( ); } else icon = QIcon(":/images/oscada_qt.png"); act = new QAction(icon, "", mn); act->setObjectName(list[iL].c_str()); //act_1->setShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_1); QObject::connect(act, SIGNAL(triggered()), this, SLOT(callQtModule())); mn->addAction(act); } //Set/update the text information string tVl = Mess->I18N(QtMod.at().modName(), lang.toStdString().c_str()); act->setText(tVl.c_str()); act->setToolTip(tVl.c_str()); tVl = Mess->I18N(QtMod.at().modInfo("Description"), lang.toStdString().c_str()); act->setWhatsThis(tVl.c_str()); } } void StApp::lastWinClose( ) { if(topLevelWindows()) return; if((!SYS->cmdOptPresent("QtInNotMainThread") && SYS->stopSignal()) || (SYS->cmdOptPresent("QtInNotMainThread") && (!mod->startCom() || mod->endRun() || SYS->stopSignal()))) quit(); else if(mod->closeToTray()) createTray(); else startDialog(); } void StApp::trayAct( QSystemTrayIcon::ActivationReason reason ) { if((reason == QSystemTrayIcon::DoubleClick || reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::MiddleClick) && !activeWindow()) startDialog(); } bool StApp::callQtModule( const string &nm ) { vector list; setProperty("closeToTray", mod->closeToTray()); AutoHD QtMod = mod->owner().modAt(nm); QMainWindow *(TModule::*openWindow)( ); QtMod.at().modFunc("QMainWindow *openWindow();",(void (TModule::**)()) &openWindow); QMainWindow *new_wnd = ((&QtMod.at())->*openWindow)( ); if(!new_wnd) return false; switch(s2i(SYS->cmdOpt("showWin"))) { case 1: new_wnd->showMaximized(); break; case 2: new_wnd->showFullScreen(); break; default: new_wnd->show(); } return true; } void StApp::saveSessState( QSessionManager &manager ) { int smd = -1; //Requesting the project name of the current session if(SYS->prjNm().empty()) sessPrjToRestore = mod->sess("", &smd); //Updating the session data else mod->sess(SYS->prjNm()); //Saving the session for the project SYS->prjNm() if(smd == TUIMod::RestartAnyway || (SYS->prjNm().size() && mod->sessCntr() == TUIMod::RestartAnyway)) manager.setRestartHint(QSessionManager::RestartAnyway); else if(smd == TUIMod::RestartImmediately || (SYS->prjNm().size() && mod->sessCntr() == TUIMod::RestartImmediately)) manager.setRestartHint(QSessionManager::RestartImmediately); else if(smd == TUIMod::RestartIfRunning || (SYS->prjNm().size() && mod->sessCntr() == TUIMod::RestartIfRunning)) manager.setRestartHint(QSessionManager::RestartIfRunning); else manager.setRestartHint(QSessionManager::RestartNever); //Disable the session control } void StApp::startDialog( ) { if(!stDlg) stDlg = new StartDialog(); switch(s2i(SYS->cmdOpt("showWin"))) { case 1: stDlg->showMaximized(); break; case 2: stDlg->showFullScreen();break; default: stDlg->show(); } } //************************************************* //* StartDialog * //************************************************* StartDialog::StartDialog( ) : logo(NULL), prjsLs(NULL), prjsBt(NULL), updTmr(NULL) { if(SYS->prjCustMode()) setWindowTitle(_("Qt-starter of OpenSCADA")); else if(SYS->prjNm().size()) setWindowTitle(QString(_("Project: %1")).arg(SYS->prjNm().c_str())); else setWindowTitle(_("OpenSCADA start")); QImage ico_t; if(SYS->prjNm().size()) ico_t.load(TUIS::icoGet(SYS->prjNm(),NULL,true).c_str()); if(ico_t.isNull() && SYS->prjCustMode()) ico_t.load(TUIS::icoGet(SYS->id(),NULL,true).c_str()); if(ico_t.isNull()) ico_t.load(":/images/oscada_qt.png"); setWindowIcon(QPixmap::fromImage(ico_t)); //Menu prepare // About "System info" if(!ico_t.load(TUIS::icoGet("help",NULL,true).c_str())) ico_t.load(":/images/help.png"); QAction *actAbout = new QAction(QPixmap::fromImage(ico_t),_("&About"),this); actAbout->setToolTip(_("Program information")); actAbout->setWhatsThis(_("The button for display the program information.")); //actAbout->setStatusTip(_("Press to display the program information.")); connect(actAbout, SIGNAL(triggered()), this, SLOT(about())); // About Qt QAction *actQtAbout = new QAction(_("About &Qt"),this); actQtAbout->setToolTip(_("Qt information")); actQtAbout->setWhatsThis(_("The button for getting the Qt information.")); //actQtAbout->setStatusTip(_("Press to get the Qt information.")); connect(actQtAbout, SIGNAL(triggered()), this, SLOT(aboutQt())); // QTStarter manual if(!ico_t.load(TUIS::icoGet("manual",NULL,true).c_str())) ico_t.load(":/images/manual.png"); QAction *actManual = new QAction(QPixmap::fromImage(ico_t),QString(_("%1 manual")).arg(mod->modId().c_str()),this); actManual->setProperty("doc", "Modules/QTStarter|Modules/QTStarter"); actManual->setShortcut(Qt::Key_F1); actManual->setWhatsThis(QString(_("The button for the manual %1.")).arg(mod->modId().c_str())); //actManual->setStatusTip(QString(_("Press for the manual %1.")).arg(mod->modId().c_str())); connect(actManual, SIGNAL(triggered()), this, SLOT(enterManual())); // OpenSCADA manual index QAction *actManualSYS = new QAction(QPixmap::fromImage(ico_t),QString(_("%1 manual")).arg(PACKAGE_STRING),this); actManualSYS->setProperty("doc", "index|Documents"); actManualSYS->setWhatsThis(QString(_("The button for the manual %1.")).arg(PACKAGE_STRING)); //actManualSYS->setStatusTip(QString(_("Press for the manual %1.")).arg(PACKAGE_STRING)); connect(actManualSYS, SIGNAL(triggered()), this, SLOT(enterManual())); // What is if(!ico_t.load(TUIS::icoGet("contexthelp",NULL,true).c_str())) ico_t.load(":/images/contexthelp.png"); QAction *actWhatIs = new QAction(QPixmap::fromImage(ico_t),_("What's &This"),this); actWhatIs->setShortcut(Qt::SHIFT|Qt::Key_F1); actWhatIs->setToolTip(_("Question about GUI elements")); actWhatIs->setWhatsThis(_("The button for requesting the information about user interface elements.")); //actWhatIs->setStatusTip(_("Press for requesting about user interface elements")); connect(actWhatIs, SIGNAL(triggered()), this, SLOT(enterWhatsThis())); // Create menu "help" QMenu *help = menuBar()->addMenu(_("&Help")); help->addAction(actAbout); help->addAction(actQtAbout); help->addAction(actManual); help->addAction(actManualSYS); help->addSeparator(); help->addAction(actWhatIs); //Append the logotype setCentralWidget(new QWidget(this)); QVBoxLayout *wnd_lay = new QVBoxLayout(centralWidget()); wnd_lay->setContentsMargins(6, 6, 6, 6); wnd_lay->setSpacing(4); QPixmap pxImg(":/images/logo.png"); if(!pxImg.isNull()) { logo = new QLabel(this); logo->setPixmap(pxImg); logo->setAutoFillBackground(true); QPalette plt = logo->palette(); plt.setBrush(QPalette::Window, pxImg.copy(pxImg.width()-1, 0, 1, pxImg.height())); logo->setPalette(plt); wnd_lay->addWidget(logo); } //Append the title and the list of Qt modules of OpenSCADA QFont labFont = font(); labFont.setBold(true); labFont.setItalic(true); if(!SYS->prjCustMode() && SYS->prjNm().size()) { QLabel *lab = new QLabel(_("Qt modules of OpenSCADA"), this); lab->setFont(labFont); wnd_lay->addWidget(lab); } if(SYS->prjCustMode() || SYS->prjNm().size()) { vector list; mod->owner().modList(list); for(unsigned iL = 0; iL < list.size(); iL++) if(mod->owner().modAt(list[iL]).at().modInfo("SubType") == "Qt" && mod->owner().modAt(list[iL]).at().modFuncPresent("QMainWindow *openWindow();")) { QIcon icon; if(mod->owner().modAt(list[iL]).at().modFuncPresent("QIcon icon();")) { QIcon (TModule::*iconGet)(); mod->owner().modAt(list[iL]).at().modFunc("QIcon icon();",(void (TModule::**)()) &iconGet); icon = ((&mod->owner().modAt(list[iL]).at())->*iconGet)( ); } else icon = QIcon(":/images/oscada_qt.png"); AutoHD QtMod = mod->owner().modAt(list[iL]); QPushButton *butt = new QPushButton(icon, QtMod.at().modName().c_str(), centralWidget()); butt->setObjectName(list[iL].c_str()); butt->setToolTip(QString(_("Call the Qt-based UI module '%1'")).arg(list[iL].c_str())); butt->setWhatsThis(QString(_("Module: %1\n" "Name: %2\n" "Source: %3\n" "Version: %4\n" "Author: %5\n" "Description: %6\n" "License: %7")).arg(QtMod.at().modInfo("Module").c_str()) .arg(QtMod.at().modInfo("Name").c_str()) .arg(QtMod.at().modInfo("Source").c_str()) .arg(QtMod.at().modInfo("Version").c_str()) .arg(QtMod.at().modInfo("Author").c_str()) .arg(QtMod.at().modInfo("Description").c_str()) .arg(QtMod.at().modInfo("License").c_str())); QObject::connect(butt, SIGNAL(clicked(bool)), mod->QtApp, SLOT(callQtModule())); wnd_lay->addWidget(butt); } //Append the horizontal line and the title of projects of OpenSCADA if(!SYS->prjCustMode()) { QFrame *gFrame = new QFrame(this); gFrame->setLineWidth(4); gFrame->setFrameShape(QFrame::HLine); gFrame->setFrameShadow(QFrame::Sunken); wnd_lay->addWidget(gFrame); QLabel *lab = new QLabel(_("Projects of OpenSCADA"), this); lab->setFont(labFont); wnd_lay->addWidget(lab); } } //Append the list of projects of OpenSCADA prjsLs = NULL; prjsBt = NULL; if(!SYS->prjCustMode()) { //bool oscd_datadir_wr = (access(oscd_datadir_full,X_OK|W_OK) == 0); // Prepare the list widget for projects selection. prjsLs = new QListWidget(this); prjsLs->setSortingEnabled(true); prjsLs->setContextMenuPolicy(Qt::CustomContextMenu); prjsLs->setToolTip(_("List of allowed to call/switch projects of OpenSCADA")); prjsLs->setWhatsThis(_("The list for call and switch to allowed projects of OpenSCADA.")); connect(prjsLs, SIGNAL(itemSelectionChanged()), this, SLOT(projSelect())); connect(prjsLs, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(projSwitch())); connect(prjsLs, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(prjsLsCtxMenuRequested(const QPoint&))); wnd_lay->addWidget(prjsLs); updatePrjList(true); prjsBt = new QPushButton(QIcon(":/images/ok.png"), SYS->prjNm().size()?_("Switch to the selected project"):_("Call the selected project"), this); prjsBt->setEnabled(false); prjsBt->setToolTip(SYS->prjNm().size()?_("Switch to the selected project"):_("Call the selected project")); prjsBt->setWhatsThis(SYS->prjNm().size()?_("The button for switch to the selected project."):_("The button for call the selected project.")); QObject::connect(prjsBt, SIGNAL(clicked(bool)), this, SLOT(projSwitch())); wnd_lay->addWidget(prjsBt); QFrame *gFrame = new QFrame(this); gFrame->setFrameShape(QFrame::HLine); gFrame->setFrameShadow(QFrame::Plain); wnd_lay->addWidget(gFrame); QPushButton *prjAddUpdt = new QPushButton(QIcon(":/images/it_add.png"), _("Create/update project"), this); prjAddUpdt->setToolTip(_("New projects creating or updating of presented ones, like to desktop links")); prjAddUpdt->setWhatsThis(_("The button for new projects creating or updating of presented ones, like to desktop links.")); QObject::connect(prjAddUpdt, SIGNAL(clicked(bool)), this, SLOT(projCreateUpdt())); wnd_lay->addWidget(prjAddUpdt); prjsLs->installEventFilter(this); } wnd_lay->addItem(new QSpacerItem(20,20,QSizePolicy::Minimum,QSizePolicy::Expanding)); QFrame *gFrame = new QFrame(this); gFrame->setLineWidth(4); gFrame->setFrameShape(QFrame::HLine); gFrame->setFrameShadow(QFrame::Sunken); wnd_lay->addWidget(gFrame); QPushButton *butt = new QPushButton(QIcon(":/images/exit.png"),_("Exit the program"), this); butt->setObjectName("*exit*"); butt->setToolTip(_("Exit the program")); butt->setWhatsThis(_("The button for exit the program.")); QObject::connect(butt, SIGNAL(clicked(bool)), mod->QtApp, SLOT(callQtModule())); wnd_lay->addWidget(butt); } void StartDialog::showEvent( QShowEvent* ) { //Hide the projects apply button and the logotype for too busy lists if(prjsLs) { if(prjsBt) prjsBt->setVisible(!prjsLs->verticalScrollBar() || !prjsLs->verticalScrollBar()->isVisible() || prjsLs->height() > 3*QFontMetrics(prjsLs->font()).height()); if(logo) logo->setVisible(!prjsLs->verticalScrollBar() || !prjsLs->verticalScrollBar()->isVisible() || prjsLs->height() > 3*QFontMetrics(prjsLs->font()).height()); } updTmr = new QTimer(this); connect(updTmr, SIGNAL(timeout()), this, SLOT(updatePrjList())); updTmr->start(1e3*prmWait_TM); } void StartDialog::closeEvent( QCloseEvent *ce ) { if(updTmr) { delete updTmr; updTmr = NULL; } if(!mod->QtApp->trayPresent() && mod->QtApp->topLevelWindows() <= 1) SYS->stop(); ce->accept(); } bool StartDialog::eventFilter( QObject *object, QEvent *event ) { if(object == prjsLs && updTmr) updTmr->start(1e3*prmWait_TM); //Delay the updating at some activity in the project list return false; } void StartDialog::updatePrjList( bool force, const string &stage ) { if(!force && (!isVisible() || !isActiveWindow())) return; if(stage.empty()) { prjsLs->blockSignals(true); for(int iIt = 0; iIt < prjsLs->count(); iIt++) prjsLs->item(iIt)->setData(Qt::UserRole+3, 0); updatePrjList(force, "user"); updatePrjList(force, "sys"); for(int iIt = 0; iIt < prjsLs->count(); iIt++) if(!prjsLs->item(iIt)->data(Qt::UserRole+3).toInt()) delete prjsLs->takeItem(iIt--); prjsLs->blockSignals(false); //Selecting the current project if(!prjsLs->selectedItems().size() && SYS->prjNm().size()) { QList sIt = prjsLs->findItems(SYS->prjNm().c_str(), Qt::MatchStartsWith); for(int iIt = 0; iIt < sIt.length(); iIt++) if(sIt[iIt]->data(Qt::UserRole).toString().toStdString() == SYS->prjNm()) { sIt[iIt]->setSelected(true); prjsLs->setCurrentItem(sIt[iIt]); } } } else { string wDir = SYS->prjUserDir(); if(stage == "sys") wDir = oscd_datadir_full; DIR *IdDir = wDir.size() ? opendir(wDir.c_str()) : NULL; if(!IdDir) return; struct stat file_stat; dirent *scan_rez = NULL, *scan_dirent = (dirent*)malloc(offsetof(dirent,d_name) + NAME_MAX + 1); while(readdir_r(IdDir,scan_dirent,&scan_rez) == 0 && scan_rez) { if(strcmp(scan_rez->d_name,"..") == 0 || strcmp(scan_rez->d_name,".") == 0) continue; // Presenting of the project's folder if(stat((wDir+"/"+scan_rez->d_name).c_str(),&file_stat) != 0 || (file_stat.st_mode&S_IFMT) != S_IFDIR) continue; // Presenting of the project's config file if(stage == "user") { string itNm = wDir + "/" + scan_rez->d_name + "/oscada.xml"; if(stat(itNm.c_str(),&file_stat) != 0 || (file_stat.st_mode&S_IFMT) != S_IFREG) continue; } else if(stage == "sys") { string itNm = string(sysconfdir_full) + "/oscada_" + scan_rez->d_name + ".xml"; if(stat(itNm.c_str(),&file_stat) != 0 || (file_stat.st_mode&S_IFMT) != S_IFREG) { itNm = wDir + "/" + scan_rez->d_name + "/oscada.xml"; if(stat(itNm.c_str(),&file_stat) != 0 || (file_stat.st_mode&S_IFMT) != S_IFREG) continue; } } // Check for presenting already QListWidgetItem *tit = NULL; QList pLs = prjsLs->findItems(scan_rez->d_name, Qt::MatchStartsWith); int iP = 0; for( ; iP < pLs.size() && pLs[iP]->data(Qt::UserRole).toString() != scan_rez->d_name; iP++) ; if(pLs.size() && iP < pLs.size()) { tit = pLs[iP]; if(tit->data(Qt::UserRole+3).toInt()) continue; } else prjsLs->addItem((tit=new QListWidgetItem("", prjsLs))); // Item placing string opt; if(scan_rez->d_name == SYS->prjNm()) opt += string(opt.size()?", ":"") + _("CURRENT"); bool isRun = false; if(access((wDir+"/"+scan_rez->d_name+"/lock").c_str(),F_OK) == 0) { opt += string(opt.size()?", ":"") + _("RUNNING"); isRun = true; } // Get the backups list vector prms; prms.push_back(string(bindir_full "/openscada-proj backupList ") + scan_rez->d_name); string bLs = SYS->objFuncCall("system", prms, "root").getS(); if(bLs.size()) { int nBkps = 0; for(int off = 0; TSYS::strLine(bLs,0,&off).size(); ) nBkps++; opt += string(opt.size()?", ":"") + TSYS::strMess(_("%d backups"),nBkps); } tit->setText((string(scan_rez->d_name)+(opt.size()?" ("+opt+")":"")).c_str()); tit->setData(Qt::UserRole, QString(scan_rez->d_name)); tit->setData(Qt::UserRole+1, isRun ? "" : bLs.c_str()); tit->setData(Qt::UserRole+2, isRun); tit->setData(Qt::UserRole+3, (int)SYS->sysTm()); QImage ico; if(ico.load((string(oscd_datadir_full "/icons/")+scan_rez->d_name+".png").c_str()) || ico.load((wDir+"/"+scan_rez->d_name+"/icons/"+scan_rez->d_name+".png").c_str())) tit->setIcon(QPixmap::fromImage(ico)); } free(scan_dirent); closedir(IdDir); } } void StartDialog::about( ) { string mess = _("%s v%s on Qt v%s.\n%s\nAuthor: %s\nLicense: %s\n\n" "%s v%s.\n%s\nLicense: %s\nAuthor: %s\nWeb site: %s"); #undef _ #define _(mess) Mess->I18N(mess).c_str() QMessageBox::about(this, windowTitle(), TSYS::strMess(mess.c_str(), _(mod->modInfo("Name")), mod->modInfo("Version").c_str(), QT_VERSION_STR, _(mod->modInfo("Description")), _(mod->modInfo("Author")), mod->modInfo("License").c_str(), PACKAGE_NAME, VERSION, _(PACKAGE_DESCR), PACKAGE_LICENSE, _(PACKAGE_AUTHOR), PACKAGE_SITE).c_str()); #undef _ #define _(mess) mod->I18N(mess).c_str() } void StartDialog::aboutQt( ) { QMessageBox::aboutQt(this, mod->modInfo("Name").c_str()); } void StartDialog::enterWhatsThis( ) { QWhatsThis::enterWhatsThisMode(); } void StartDialog::enterManual( ) { string findDoc = TUIS::docGet(sender()->property("doc").toString().toStdString()); if(findDoc.size()) system(findDoc.c_str()); else QMessageBox::information(this, _("Manual"), QString(_("No manual '%1' found offline or online!")).arg(sender()->property("doc").toString())); } void StartDialog::projCreateUpdt( ) { bool ok = false; QString prjNm = QInputDialog::getText(this, _("New project or project to update"), _("Project name for new one creating or to update present one:"), QLineEdit::Normal, "NewProject", &ok); if(!ok) return; if(!prjNm.length()) { QMessageBox::warning(this, _("New project or project to update"), _("Empty name of the project is unavailable!")); return; } projSwitch(prjNm); } void StartDialog::projSelect( ) { if(!prjsLs || !prjsBt) return; QList selPrj = prjsLs->selectedItems(); prjsBt->setEnabled(selPrj.length() && (prjsLs->row(selPrj[0]) == 0 || selPrj[0]->data(Qt::UserRole).toString().toStdString() != SYS->prjNm())); } void StartDialog::projSwitch( const QString& iprj ) { if((!prjsLs || !prjsBt) && !iprj.length()) return; QString prjNm = iprj; bool toCreate = true; if(!prjNm.length()) { QList selPrj = prjsLs->selectedItems(); if(!selPrj.length()) return; prjNm = selPrj[0]->data(Qt::UserRole).toString(); toCreate = false; } //Switch to presented project and create new one before if(prjNm.toStdString() == SYS->prjNm()) return; if(SYS->prjNm().size() && QMessageBox::warning(this,_("Switch project"), QString(_("Do you really want to change the current project \"%1\" to \"%2\"?")).arg(SYS->prjNm().c_str()).arg(prjNm), QMessageBox::Yes|QMessageBox::No,QMessageBox::No) != QMessageBox::Yes) return; if((access(((oscd_datadir_full "/")+prjNm.toStdString()+"/lock").c_str(),F_OK) == 0 || access((SYS->prjUserDir()+"/"+prjNm.toStdString()+"/lock").c_str(),F_OK) == 0) && QMessageBox::warning(this,SYS->prjNm().size()?_("Switch project"):_("Call project"), QString(SYS->prjNm().size()?_("Project \"%1\" seems running now! You still want to switch the project?"): _("Project \"%1\" seems running now! You still want to call the project?")).arg(prjNm), QMessageBox::Yes|QMessageBox::No,QMessageBox::No) != QMessageBox::Yes) return; if(!SYS->prjSwitch(prjNm.toStdString(),toCreate)) QMessageBox::warning(this, SYS->prjNm().size()?_("Switch project"):_("Call project"), QString(_("Project \"%1\" seems wrong or broken!")).arg(prjNm)); else mod->sess(prjNm.toStdString()); } void StartDialog::prjsLsCtxMenuRequested( const QPoint &pos ) { QMenu *menu = new QMenu(); //prjsLs->createStandardContextMenu(); //The BackUp action QAction *actBackUp = new QAction(_("BackUp"), this); menu->addAction(actBackUp); //The restoring actions if(prjsLs->currentItem()->data(Qt::UserRole+1).toString().size()) { menu->addSeparator(); QAction *actRest = NULL; string backId, backs = prjsLs->currentItem()->data(Qt::UserRole+1).toString().toStdString(); for(int off = 0; (backId=TSYS::strLine(backs,0,&off)).size(); ) { actRest = new QAction(QString(_("Restore from \"%1\"")).arg(backId.c_str()), this); actRest->setObjectName(backId.c_str()); menu->addAction(actRest); } } //Extra project actions QAction *actRemove = NULL; if(!prjsLs->currentItem()->data(Qt::UserRole+2).toBool()) { actRemove = new QAction(QIcon(":/images/it_del.png"), _("Remove"), this); menu->addSeparator(); menu->addAction(actRemove); } QAction *rez = menu->exec(QCursor::pos()); if(actRemove && rez == actRemove && QMessageBox::warning(this,_("Remove project"), QString(_("Do you really want to remove the project \"%1\"?")).arg(prjsLs->currentItem()->data(Qt::UserRole).toString()), QMessageBox::Yes|QMessageBox::No,QMessageBox::No) == QMessageBox::Yes) { vector prms; prms.push_back(string(bindir_full "/openscada-proj remove ") + prjsLs->currentItem()->data(Qt::UserRole).toString().toStdString()); SYS->objFuncCall("system", prms, "root").getS(); updatePrjList(true); } else if(rez == actBackUp && prjsLs->currentItem()) { vector prms; prms.push_back(string(bindir_full "/openscada-proj backup ") + prjsLs->currentItem()->data(Qt::UserRole).toString().toStdString()); prms.push_back(true); QApplication::setOverrideCursor(Qt::WaitCursor); SYS->objFuncCall("system", prms, "root").getS(); QApplication::restoreOverrideCursor(); updatePrjList(true); } else if(rez && rez->objectName().size()) { vector prms; prms.push_back(string(bindir_full "/openscada-proj backupRestore ") + prjsLs->currentItem()->data(Qt::UserRole).toString().toStdString() + " " + rez->objectName().toStdString()); prms.push_back(true); QApplication::setOverrideCursor(Qt::WaitCursor); SYS->objFuncCall("system", prms, "root").getS(); QApplication::restoreOverrideCursor(); } menu->deleteLater(); } //************************************************* //* I18NTranslator * //************************************************* I18NTranslator::I18NTranslator( ) : QTranslator(0) { } bool I18NTranslator::isEmpty( ) const { return false; } #if QT_VERSION < 0x050000 QString I18NTranslator::translate( const char *context, const char *sourceText, const char *comment ) const #else QString I18NTranslator::translate( const char *context, const char *sourceText, const char *comment, int n ) const #endif { if(!sourceText) return ""; QString trRes = mod->I18N(sourceText, qApp->property("lang").toString().toStdString().c_str()).c_str(); if(mess_lev() == TMess::Debug && trRes == sourceText) mess_debug(mod->nodePath().c_str(),_("Untranslated Qt message: '%s'"),sourceText); return trRes; }