mainwindow.cpp #1

  • //
  • guest/
  • james_creasy/
  • ClientKit/
  • mainwindow.cpp
  • View
  • Commits
  • Open Download .zip Download (8 KB)
//
// ClientKit for Perforce
//
// Easily create custom Perforce clients with a wide range of capabilities using Qt and web technologies
//
// The starting page for a web application can be stored in a web server, a local file,  
// a Perforce server, or a QResource. A QResource could be complied into the EXE for maximum security,
// or linked at runtime. The EXE could be constrained to accept QResource files that contain a matching 
// secret key.
//
// A function pointer exposed to the JavaScript provides API access to both configuring the ClientKit 
// container and running Perforce server commands
//
// This program should make use of no proprietary Perforce knowledge,
// and should be able to be duplicated by a programmer with no special Perforce knowledge.
//
// Thin Client, from  http://doc.qt.nokia.com/4.7-snapshot/qtwebkit-bridge.html
//    Another use case is using Qt as a native backend to a full web application, referred to
//    here as a thin client. In this use-case, the entire UI is driven by HTML, JavaScript and 
//    CSS, and native Qt-based components are used to allow that application access to native 
//    features not usually exposed to the web, or to enable helper components that are best
//    written with C++.
//

#include "mainwindow.h"
#include <QVBoxLayout>
#include <QLabel>

#include <QtWebKit>
#include <QFrame>
#include <QUrl>
#include <QFile>
#include <QVariant>
#include <QDir>
#include <QCoreApplication>
#include <iostream>

#include "clientapi.h"
#include "enviro.h"

#pragma comment(lib, "winmm.lib")	// these 3 for building on windows
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "advapi32.lib")

#include "KCommon.h"

static const QString P4PointerName = "p4";

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    mMainWidget = new QFrame( this );
    QVBoxLayout* pageLayout = new QVBoxLayout();
    QWebView* webview = new QWebView(this);
    mWindowStyle = "normal";
    mWindowStyles << "normal" << "transparent" << "invisible";
    
	mWebPage = new QWebPage(this);        
	// Manage capabilities of JavaScript code here to fit your security model
    mWebPage->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
    mWebPage->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
    mWebPage->settings()->setAttribute(QWebSettings::JavascriptCanAccessClipboard, true);
    mWebPage->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true);
    mWebPage->settings()->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, true);
    mWebPage->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
//    mWebPage->settings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
//    mWebPage->settings()->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, true);
    mWebPage->settings()->setAttribute(QWebSettings::SpatialNavigationEnabled, true);

	createClientKitApi(P4PointerName);

    QWebFrame* frame = mWebPage->mainFrame();
	mWebPage->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);	// add to API
    connect(frame, SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(handleJavaScriptWindowObjectCleared()));

	webview->setPage(mWebPage);  

	// Bootstrap: 1. first we look for a configuration file in .././conf/clientkit.conf from the .exe file location
	QString appDirPath = QCoreApplication::applicationDirPath();
	QDir confDir = QDir(appDirPath);
	confDir.cdUp();
	QString confFilename = confDir.filePath("clientkit.conf");	// once clientkit.exe is installed in a \bin dir, change this to look in a sibling dir called 'config'

	QString startFilename;
	QString startFileContents;
	if (QFile::exists(confFilename))
	{
		QString confFileContents = readFile(confFilename);
	
		QMap<QString, QString> settingsMap = parseConfFile(confFileContents);
		if (settingsMap.contains("startFile"))
			startFilename = settingsMap["startFile"];
	}
	else
	{
		// Bootstrap: 2. Then we look for an index.html in the current directory
		QString currentDir = QDir::currentPath();
		QString defaultIndex = currentDir + "/" + "index.html";	// fixme: don't do string manipulation
		startFilename = defaultIndex;
	}

	// Here we have the filename we want to try to load
	QFile startFile(startFilename);

	// Security tip: constrain the type of file that can be loaded here. For example, could disallow loading local files

	if (!startFile.exists()) // Bootstrap: 3. use default message
	{	
		startFileContents = "<html><body><h2>ClientKit did not find a clientkit.conf file in the executable directory or an index.html file in the current directory</h2><p><br>Default startup file not found:"+startFilename+"</p><br>Create this file to define your start page<p><p>You can also create a clientkit.conf file and define<p><p>startFile=<My filepath to an html file><p></body></html>";
        webview->setHtml(startFileContents);
    }
	else
		webview->setUrl(startFilename);

    pageLayout->setContentsMargins(0,0,0,0);
    pageLayout->addWidget(webview);
    mMainWidget->setLayout(pageLayout);
    setCentralWidget( mMainWidget );
}

MainWindow::~MainWindow()
{
}

QMap<QString, QString> MainWindow::parseConfFile(QString fileContents)
{
	QMap<QString, QString> configMap;
	QStringList lines = fileContents.split(QRegExp("(\\r\\n)|(\\n\\r)|\\r|\\n"), QString::SkipEmptyParts);

    for (int i = 0; i < lines.size(); ++i)
	{
         QString line = lines.at(i).toLocal8Bit().constData();
		 if (line[0] != '#')	// ignore comment lines
		 {
			line.trimmed();
			if (!line.isEmpty())
			{
				int pos = line.indexOf(" ");
				if (pos == -1)
					continue;
				QString key = line.left(pos);
				QString value = line.mid(pos+1);
				configMap.insert(key, value);
			}
		}
	}

	return configMap;
}

bool
MainWindow::createClientKitApi(QString pointerName)
{
	ClientKitApi* clientKitApi = new ClientKitApi(this, this, pointerName);
	mClientKitApiMap.insert(pointerName, clientKitApi);
    mWebPage->mainFrame()->addToJavaScriptWindowObject(pointerName, clientKitApi);
	return true;
}

bool
MainWindow::setWindowStyle(QString styleName)
{
    Qt::WindowFlags flags;
    
    if (styleName=="transparent")
    {
        setStyleSheet("background:transparent;");
        setAttribute(Qt::WA_TranslucentBackground, false);
        flags = Qt::Window;
    }
    else if (styleName=="normal")
    {
        setStyleSheet("background:white;");
        setAttribute(Qt::WA_TranslucentBackground, false);
        flags = Qt::Window;
    }
    else if (styleName=="invisible")
    {
        setStyleSheet("background:transparent;");
        setAttribute(Qt::WA_TranslucentBackground, true);
        flags = Qt::FramelessWindowHint;
    }
    
    this->setWindowFlags(flags);
    this->show();
    
    mWindowStyle = styleName;
    
    return true;
}

void
MainWindow::handleJavaScriptWindowObjectCleared()
{
    QWebFrame* frame = mWebPage->mainFrame();

	QMapIterator<QString, ClientKitApi*> mapi(mClientKitApiMap);
	while (mapi.hasNext()) 
	{
		mapi.next();
		frame->addToJavaScriptWindowObject(mapi.key(), mapi.value());	// This the magic that makes the pointer visible to JavaScript
//		can use evaluateJavaScript here to add JavaScript to execution context
	}

    QList<QWebFrame *> childFrames = frame->childFrames();
    for(int i = 0; i < childFrames.size(); i++)
    {
		QMapIterator<QString, ClientKitApi*> mapi(mClientKitApiMap);
		while (mapi.hasNext()) 
		{
			mapi.next();		
			frame->addToJavaScriptWindowObject(mapi.key(), mapi.value());  
//			can use evaluateJavaScript here to add JavaScript to execution context
		}
    }
}

QString
MainWindow::readFile(QString filename)	// currently only reads a local file
{
	QString filecontents;            
    QFile f( filename );
    if ( !f.exists() )
    {
		mMessageLog += "File not found: " + filename;
        return QString();
    }
    f.open( QIODevice::ReadOnly );
    QTextStream ts ( &f );
    QTextCodec * codec = QTextCodec::codecForName("UTF-8");
    ts.setCodec( codec );
    filecontents = ts.readAll();

    return filecontents;  
} 
# Change User Description Committed
#1 8060 james_creasy Client Kit for Perforce