<commit_before>// Copyright 2016 ESRI
//
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
//
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
//
// See the Sample code usage restrictions document for further information.
//

#include "ToolManager.h"
#include "AbstractTool.h"
#include "ToolResourceProvider.h"

namespace Esri
{
namespace ArcGISRuntime
{
namespace Toolkit
{

ToolManager& ToolManager::instance()
{
  static ToolManager instance;

  return instance;
}

ToolManager::ToolManager()
{

}

ToolManager::~ToolManager()
{

}

void ToolManager::addTool(AbstractTool* tool)
{
  if (!tool)
    return;

  m_tools.insert(tool->toolName(), tool);
}

AbstractTool* ToolManager::tool(const QString& toolName) const
{
  return m_tools[toolName];
}

ToolManager::ToolsList::iterator ToolManager::begin()
{
  return m_tools.begin();
}

ToolManager::ToolsList::iterator ToolManager::end()
{
  return m_tools.end();
}

ToolManager::ToolsList::const_iterator ToolManager::begin() const
{
  return m_tools.cbegin();
}

ToolManager::ToolsList::const_iterator ToolManager::end() const
{
  return m_tools.cend();
}

} // Toolkit
} // ArcGISRuntime
} // Esri
<commit_msg>Add API ref and remove tools when they are destroyed<commit_after>// Copyright 2016 ESRI
//
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
//
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
//
// See the Sample code usage restrictions document for further information.
//

#include "ToolManager.h"
#include "AbstractTool.h"
#include "ToolResourceProvider.h"

namespace Esri
{
namespace ArcGISRuntime
{
namespace Toolkit
{

/*! \brief Returns the singleton instance of the tool manager.
 *
 */
ToolManager& ToolManager::instance()
{
  static ToolManager instance;

  return instance;
}

/*! \internal.
 *
 */
ToolManager::ToolManager()
{

}

/*! \brief Destructor.
 *
 */
ToolManager::~ToolManager()
{

}

/*! \brief Adds \a tool to the manager.
 *
 * The \l AbstractTool can be retrieved from the manager by supplying
 * the tool name or by requesting it by type.
 */
void ToolManager::addTool(AbstractTool* tool)
{
  if (!tool)
    return;

  // when a tool is destroyed, remove it from the manager
  QObject::connect(tool, &AbstractTool::destroyed, tool, [this, tool]()
  {
    auto it = m_tools.begin();
    auto itEnd = m_tools.end();
    for(; it != itEnd; ++it)
    {
      if (it.value() == tool)
      {
        m_tools.erase(it);
        break;
      }
    }
  });

  m_tools.insert(tool->toolName(), tool);
}

/*! \brief Retrieve the \l AbsgtractTool with the name \a toolName.
 *
 * return \c nullptr if the tool cannot be found.
 */
AbstractTool* ToolManager::tool(const QString& toolName) const
{
  return m_tools[toolName];
}

/*! \brief Returns a begin iterator to the list of tools.
 *
 */
ToolManager::ToolsList::iterator ToolManager::begin()
{
  return m_tools.begin();
}

/*! \brief Returns an end iterator to the list of tools.
 *
 */
ToolManager::ToolsList::iterator ToolManager::end()
{
  return m_tools.end();
}

/*! \brief Returns a begin (const) iterator to the list of tools.
 *
 */
ToolManager::ToolsList::const_iterator ToolManager::begin() const
{
  return m_tools.cbegin();
}

/*! \brief Returns an end (const) iterator to the list of tools.
 *
 */
ToolManager::ToolsList::const_iterator ToolManager::end() const
{
  return m_tools.cend();
}

} // Toolkit
} // ArcGISRuntime
} // Esri
<|endoftext|>


<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#include "condor_common.h"
#include "daemon.h"
#include "condor_debug.h"
#include "condor_daemon_core.h"
#include "subsystem_info.h"
#include "file_transfer.h"

using namespace std;

/*
class DCCached : public Daemon
{
	DCCached() :
	Daemon(DT_GENERIC)
	{}

	~DCCached() {}

	bool CreateCacheDir(string &cacheDir, time_t &leaseExpiration,
		string version);
	bool RemoveCacheDir(string cacheDir, string version);
	bool UpdateLease(string leaseID, time_t &leaseExpiration,
		string version);
};

*/

class CacheDaemon : public Service
{

  public:
        // ctor/dtor
        CacheDaemon( void ) {}
        ~CacheDaemon( void ) {}

         // Command handlers
        int commandHandler_Test(int command, Stream *stream);
        int reaper_handler(int pid, int exit_status);
};

// static ClassAd jobad;
// static FileTransfer filetrans;

int
CacheDaemon::commandHandler_Test(int command, Stream *stream)
{
	dprintf(D_ALWAYS, "entered command handler = %d\n", command);
	char *data = NULL;
	int sendval = 59;
	stream->decode();
	stream->code(data);
	stream->end_of_message();
	stream->encode();
	stream->code(&sendval);
	stream->end_of_message();
	dprintf(D_ALWAYS, "received data = %s\n", data);

	typedef struct stat Stat;
	Stat st;
	const char *path = "/scratch/cvuosalo/receive";
	 if (stat(path, &st) != 0) {
        /* Directory does not exist. EEXIST for race condition */
        if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 && errno != EEXIST)
            dprintf(D_ALWAYS, "cannot create dir = %s\n", path);
     }
	char fname[500];
	strcpy(fname, path);
	strcat(fname, "/file.out");
	int fdesc = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IRGRP | S_IROTH);
	if (fdesc > -1)
	{
		write(fdesc, data, strlen(data));
		close(fdesc);
	}
	return (TRUE);
}

typedef map<string, unsigned char *> stringMap;

void getJobAd(ClassAd *const jobad, stringMap *const hashmap)
{
	const int MAXLNLEN = (PATH_MAX * 10) + 1;
	MyString jobadln;
	char filelist[MAXLNLEN];
	while (jobadln.readLine(stdin) && jobadln != "***") {
		if (jobadln.size() > 1) {	// More than just newline
			// printf("line = %s.\n", jobadln.Value());
			if (jobadln[0] != '#' && jobad->Insert(jobadln.Value()) == false) {
				printf("ClassAd Insert failed. Exiting.\n");
				return;
			}
			if (strncmp(jobadln.Value(), "TransferInput =", 15) == 0) {
				if (sscanf(jobadln.Value(), "TransferInput = \"%s", filelist) == 1) {
					if (filelist[strlen(filelist) - 1] == '"')
						filelist[strlen(filelist) - 1] = '\0';
					// printf("filelist = %s.\n", filelist);
					char filenam[PATH_MAX + 1], restlist[MAXLNLEN];
					restlist[0] = '\0';
					while(sscanf(filelist, "%[^,],%s", filenam, restlist) == 2 ||
						sscanf(filelist, "%[^,]", filenam) == 1) {
						// printf("file = %s, rest = %s.\n", filenam, restlist);
						strcpy(filelist, restlist);
						restlist[0] = '\0';
						unsigned char result[100];
						memcpy(result,
							Condor_MD_MAC::computeOnce((unsigned char *) filenam, strlen(filenam)),
							17);
						// printf("output = ");
						for (int ind = 0; ind < 16; ++ind)
							printf("%x", result[ind]);
						printf("\n");
						hashmap->insert(pair<string, unsigned char *>(filenam, result));
					}
					printf("file = %s.\n", filenam);
				}
			}
		}
	}
	printf("line = %s.\n", jobadln.Value());
	printf("map size = %ld\n", hashmap->size());
	printf("classad size = %d\n", jobad->size());
}

static bool initSocket(const char *const TransSock,
  const char *const TransKey, ReliSock &sock)
{
  sock.timeout(30);
  if (IsDebugLevel(D_COMMAND)) {
	  dprintf (D_COMMAND, "FTClient(%s,...) making connection to %s\n",
		  getCommandStringSafe(FILETRANS_DOWNLOAD), TransSock ? TransSock : "NULL");
  }
  Daemon d( DT_ANY, TransSock );
  if ( !d.connectSock(&sock,0) ) {
	  dprintf( D_ALWAYS, "FileTransfer: Unable to connect to server "
			   "%s\n", TransSock );
	  return FALSE;
  }
  CondorError err_stack;
  if ( !d.startCommand(FILETRANS_DOWNLOAD, &sock, 30, &err_stack, NULL, false, NULL) ) {
	  dprintf( D_ALWAYS, "Failed to send download command\n");
 	  return false;
  }
  sock.encode();
  if ( !sock.put_secret(TransKey) ||
	!sock.end_of_message() ) {
	dprintf( D_ALWAYS, "Failed to send transkey\n");
	return false;
  }
  dprintf( D_FULLDEBUG, "Sent TransKey=%s\n", TransKey );
  return true;
}

//-------------------------------------------------------------

void usage(char* name)
{
	dprintf(D_ALWAYS,"Usage: %s [-f] [-b] [-t] [-p <port>]\n",name );
	exit( 1 );
}

//-------------------------------------------------------------

int CacheDaemon::reaper_handler(int pid, int exit_status)
{
	if( WIFSIGNALED(exit_status) ) {
	   dprintf( D_ALWAYS, "Unknown process exited, pid=%d, signal=%d\n", pid,
						WTERMSIG(exit_status) );
	} else {
		dprintf( D_ALWAYS, "Unknown process exited, pid=%d, status=%d\n", pid,
						WEXITSTATUS(exit_status) );
	}
	return TRUE;
}

void main_init(int argc, char *argv[])
{
	if(argc > 3)
	{
		usage(argv[0]);
	}
	dprintf(D_ALWAYS, "main_init() called\n");
	static CacheDaemon service;
	if (daemonCore != NULL) {
		dprintf(D_ALWAYS, "register ret = %d\n",
			daemonCore->Register_Command(53, "TEST_COMMAND",
			(CommandHandlercpp)&CacheDaemon::commandHandler_Test,
			"command_handler", (Service *) &service));
	} else dprintf(D_ALWAYS, "null pointer\n");

	daemonCore->Register_Reaper("Reaper",
        (ReaperHandlercpp)&CacheDaemon::reaper_handler, "Reaper",
        (Service *) &service);

	static ClassAd jobad;
	stringMap hashmap;
	getJobAd(&jobad, &hashmap);
	string tkey;
	MyString tkeyin = "TransferKey = \"";
	if (argc >= 2) {
		tkeyin += argv[1];
		tkeyin += "\"";
		dprintf(D_ALWAYS, "Adding transfer key %s\n", tkeyin.c_str());
	} else {
		dprintf(D_ALWAYS, "Need transfer key argument.\n");
		return;
	}
	if (jobad.Insert(tkeyin.Value()) == false) {
		printf("tkey Insert failed. Exiting.\n");
		return;
	}

	if (jobad.LookupString( ATTR_TRANSFER_KEY, tkey ))
		dprintf(D_ALWAYS, "Transfer key = %s\n", tkey.c_str());
	else {
	  dprintf(D_ALWAYS, "No transfer key\n");
	  return;
	}
	string tsock;
	if (jobad.LookupString( ATTR_TRANSFER_SOCKET, tsock ))
		dprintf(D_ALWAYS, "Socket = %s\n", tsock.c_str());
	else {
	  dprintf(D_ALWAYS, "No socket\n");
	  return;
	}
	ReliSock sock;
        if (initSocket(tsock.c_str(), tkey.c_str(), sock)) {
	  dprintf(D_ALWAYS, "Beginning FT:SimpleInit()\n");
	  FileTransfer filetrans;
	  if (filetrans.SimpleInit(&jobad, false, false, &sock, PRIV_USER) == 0) {
      // Don't check perms, client not server, don't use file catalog, no spool.
	  // if (filetrans.Init(&jobad, false, PRIV_USER, false) == 0) {
		  // Don't check perms, don't use file catalog.
		  dprintf(D_ALWAYS, "Can't init filetrans.\n");
		  return;
	  }
	  int retval;
	  if ((retval = filetrans.UploadFiles()) == 0) { // blocking
		  printf("Can't upload.\n");
		  return;
	  } else printf("Upload OK = %d\n", retval);
	}
}

//-------------------------------------------------------------

void main_config()
{
	dprintf(D_ALWAYS, "main_config() called\n");
}

//-------------------------------------------------------------

void main_shutdown_fast()
{
	dprintf(D_ALWAYS, "main_shutdown_fast() called\n");
	DC_Exit(0);
}

//-------------------------------------------------------------

void main_shutdown_graceful()
{
	dprintf(D_ALWAYS, "main_shutdown_graceful() called\n");
	DC_Exit(0);
}

int
main( int argc, char **argv )
{
	set_mySubSystem("DAEMONCORECLIENT", SUBSYSTEM_TYPE_DAEMON );

	dc_main_init = main_init;
	dc_main_config = main_config;
	dc_main_shutdown_fast = main_shutdown_fast;
	dc_main_shutdown_graceful = main_shutdown_graceful;

	return dc_main( argc, argv );
}
<commit_msg>Fixed to download files a client with full Init()<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#include "condor_common.h"
#include "daemon.h"
#include "condor_debug.h"
#include "condor_daemon_core.h"
#include "subsystem_info.h"
#include "file_transfer.h"

using namespace std;

/*
class DCCached : public Daemon
{
	DCCached() :
	Daemon(DT_GENERIC)
	{}

	~DCCached() {}

	bool CreateCacheDir(string &cacheDir, time_t &leaseExpiration,
		string version);
	bool RemoveCacheDir(string cacheDir, string version);
	bool UpdateLease(string leaseID, time_t &leaseExpiration,
		string version);
};

*/

class CacheDaemon : public Service
{

  public:
        // ctor/dtor
        CacheDaemon( void ) {}
        ~CacheDaemon( void ) {}

         // Command handlers
        int commandHandler_Test(int command, Stream *stream);
        int reaper_handler(int pid, int exit_status);
};

// static ClassAd jobad;
// static FileTransfer filetrans;

int
CacheDaemon::commandHandler_Test(int command, Stream *stream)
{
	dprintf(D_ALWAYS, "entered command handler = %d\n", command);
	char *data = NULL;
	int sendval = 59;
	stream->decode();
	stream->code(data);
	stream->end_of_message();
	stream->encode();
	stream->code(&sendval);
	stream->end_of_message();
	dprintf(D_ALWAYS, "received data = %s\n", data);

	typedef struct stat Stat;
	Stat st;
	const char *path = "/scratch/cvuosalo/receive";
	 if (stat(path, &st) != 0) {
        /* Directory does not exist. EEXIST for race condition */
        if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 && errno != EEXIST)
            dprintf(D_ALWAYS, "cannot create dir = %s\n", path);
     }
	char fname[500];
	strcpy(fname, path);
	strcat(fname, "/file.out");
	int fdesc = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IRGRP | S_IROTH);
	if (fdesc > -1)
	{
		write(fdesc, data, strlen(data));
		close(fdesc);
	}
	return (TRUE);
}

typedef map<string, unsigned char *> stringMap;

void getJobAd(ClassAd *const jobad, stringMap *const hashmap)
{
	const int MAXLNLEN = (PATH_MAX * 10) + 1;
	MyString jobadln;
	char filelist[MAXLNLEN];
	while (jobadln.readLine(stdin) && jobadln != "***") {
		if (jobadln.size() > 1) {	// More than just newline
			// printf("line = %s.\n", jobadln.Value());
			if (jobadln[0] != '#' && jobad->Insert(jobadln.Value()) == false) {
				printf("ClassAd Insert failed. Exiting.\n");
				return;
			}
			if (strncmp(jobadln.Value(), "TransferInput =", 15) == 0) {
				if (sscanf(jobadln.Value(), "TransferInput = \"%s", filelist) == 1) {
					if (filelist[strlen(filelist) - 1] == '"')
						filelist[strlen(filelist) - 1] = '\0';
					// printf("filelist = %s.\n", filelist);
					char filenam[PATH_MAX + 1], restlist[MAXLNLEN];
					restlist[0] = '\0';
					while(sscanf(filelist, "%[^,],%s", filenam, restlist) == 2 ||
						sscanf(filelist, "%[^,]", filenam) == 1) {
						// printf("file = %s, rest = %s.\n", filenam, restlist);
						strcpy(filelist, restlist);
						restlist[0] = '\0';
						unsigned char result[100];
						memcpy(result,
							Condor_MD_MAC::computeOnce((unsigned char *) filenam, strlen(filenam)),
							17);
						// printf("output = ");
						for (int ind = 0; ind < 16; ++ind)
							printf("%x", result[ind]);
						printf("\n");
						hashmap->insert(pair<string, unsigned char *>(filenam, result));
					}
					printf("file = %s.\n", filenam);
				}
			}
		}
	}
	printf("line = %s.\n", jobadln.Value());
	printf("map size = %ld\n", hashmap->size());
	printf("classad size = %d\n", jobad->size());
}

static bool initSocket(const char *const TransSock,
  const char *const TransKey, ReliSock &sock)
{
  sock.timeout(30);
  if (IsDebugLevel(D_COMMAND)) {
	  dprintf (D_COMMAND, "FTClient(%s,...) making connection to %s\n",
		  getCommandStringSafe(FILETRANS_DOWNLOAD), TransSock ? TransSock : "NULL");
  }
  Daemon d( DT_ANY, TransSock );
  if ( !d.connectSock(&sock,0) ) {
	  dprintf( D_ALWAYS, "FileTransfer: Unable to connect to server "
			   "%s\n", TransSock );
	  return FALSE;
  }
  CondorError err_stack;
  if ( !d.startCommand(FILETRANS_DOWNLOAD, &sock, 30, &err_stack, NULL, false, NULL) ) {
	  dprintf( D_ALWAYS, "Failed to send download command\n");
 	  return false;
  }
  sock.encode();
  if ( !sock.put_secret(TransKey) ||
	!sock.end_of_message() ) {
	dprintf( D_ALWAYS, "Failed to send transkey\n");
	return false;
  }
  dprintf( D_FULLDEBUG, "Sent TransKey=%s\n", TransKey );
  return true;
}

//-------------------------------------------------------------

void usage(char* name)
{
	dprintf(D_ALWAYS,"Usage: %s [-f] [-b] [-t] [-p <port>]\n",name );
	exit( 1 );
}

//-------------------------------------------------------------

int CacheDaemon::reaper_handler(int pid, int exit_status)
{
	if( WIFSIGNALED(exit_status) ) {
	   dprintf( D_ALWAYS, "Unknown process exited, pid=%d, signal=%d\n", pid,
						WTERMSIG(exit_status) );
	} else {
		dprintf( D_ALWAYS, "Unknown process exited, pid=%d, status=%d\n", pid,
						WEXITSTATUS(exit_status) );
	}
	return TRUE;
}

void main_init(int argc, char *argv[])
{
	if(argc > 3)
	{
		usage(argv[0]);
	}
	dprintf(D_ALWAYS, "main_init() called\n");
	static CacheDaemon service;
	if (daemonCore != NULL) {
		dprintf(D_ALWAYS, "register ret = %d\n",
			daemonCore->Register_Command(53, "TEST_COMMAND",
			(CommandHandlercpp)&CacheDaemon::commandHandler_Test,
			"command_handler", (Service *) &service));
	} else dprintf(D_ALWAYS, "null pointer\n");

	daemonCore->Register_Reaper("Reaper",
        (ReaperHandlercpp)&CacheDaemon::reaper_handler, "Reaper",
        (Service *) &service);

	static ClassAd jobad;
	stringMap hashmap;
	getJobAd(&jobad, &hashmap);
	string tkey;
	MyString tkeyin = "TransferKey = \"";
	if (argc >= 2) {
		tkeyin += argv[1];
		tkeyin += "\"";
		dprintf(D_ALWAYS, "Adding transfer key %s\n", tkeyin.c_str());
	} else {
		dprintf(D_ALWAYS, "Need transfer key argument.\n");
		return;
	}
	if (jobad.Insert(tkeyin.Value()) == false) {
		printf("tkey Insert failed. Exiting.\n");
		return;
	}

	if (jobad.LookupString( ATTR_TRANSFER_KEY, tkey ))
		dprintf(D_ALWAYS, "Transfer key = %s\n", tkey.c_str());
	else {
	  dprintf(D_ALWAYS, "No transfer key\n");
	  return;
	}
	string tsock;
	if (jobad.LookupString( ATTR_TRANSFER_SOCKET, tsock ))
		dprintf(D_ALWAYS, "Socket = %s\n", tsock.c_str());
	else {
	  dprintf(D_ALWAYS, "No socket\n");
	  return;
	}
/*
	ReliSock sock;
        if (initSocket(tsock.c_str(), tkey.c_str(), sock)) {
	  dprintf(D_ALWAYS, "Beginning FT:SimpleInit()\n");
	  FileTransfer filetrans;
	  if (filetrans.SimpleInit(&jobad, false, false, &sock, PRIV_USER) == 0) {
      // Don't check perms, client not server, don't use file catalog, no spool.
	  // if (filetrans.Init(&jobad, false, PRIV_USER, false) == 0) {
		  // Don't check perms, don't use file catalog.
		  dprintf(D_ALWAYS, "Can't init filetrans.\n");
		  return;
	  }
	  int retval;
	  if ((retval = filetrans.UploadFiles()) == 0) { // blocking
		  printf("Can't upload.\n");
		  return;
	  } else printf("Upload OK = %d\n", retval);
*/
	  FileTransfer filetrans;
	  if (filetrans.Init(&jobad, false, PRIV_USER, false) == 0) {
		  // Don't check perms, don't use file catalog.
		  dprintf(D_ALWAYS, "Can't init filetrans.\n");
		  return;
	  }
	  int retval;
	  if ((retval = filetrans.DownloadFiles()) == 0) { // blocking
		  printf("Can't Download.\n");
		  return;
	  } else printf("Download OK = %d\n", retval);
}

//-------------------------------------------------------------

void main_config()
{
	dprintf(D_ALWAYS, "main_config() called\n");
}

//-------------------------------------------------------------

void main_shutdown_fast()
{
	dprintf(D_ALWAYS, "main_shutdown_fast() called\n");
	DC_Exit(0);
}

//-------------------------------------------------------------

void main_shutdown_graceful()
{
	dprintf(D_ALWAYS, "main_shutdown_graceful() called\n");
	DC_Exit(0);
}

int
main( int argc, char **argv )
{
	set_mySubSystem("DAEMONCORECLIENT", SUBSYSTEM_TYPE_DAEMON );

	dc_main_init = main_init;
	dc_main_config = main_config;
	dc_main_shutdown_fast = main_shutdown_fast;
	dc_main_shutdown_graceful = main_shutdown_graceful;

	return dc_main( argc, argv );
}
<|endoftext|>


<commit_before>#pragma once

// `krbn::receiver` can be used safely in a multi-threaded environment.

#include "console_user_server_client.hpp"
#include "constants.hpp"
#include "device_grabber.hpp"
#include "dispatcher.hpp"
#include "grabbable_state_queues_manager.hpp"
#include "local_datagram/server_manager.hpp"
#include "session.hpp"
#include "types.hpp"
#include <vector>

namespace krbn {
class receiver final : public pqrs::dispatcher::extra::dispatcher_client {
public:
  receiver(const receiver&) = delete;

  receiver(std::weak_ptr<grabbable_state_queues_manager> weak_grabbable_state_queues_manager) : dispatcher_client(),
                                                                                weak_grabbable_state_queues_manager_(weak_grabbable_state_queues_manager) {
    std::string socket_file_path(constants::get_grabber_socket_file_path());

    unlink(socket_file_path.c_str());

    size_t buffer_size = 32 * 1024;
    std::chrono::milliseconds server_check_interval(3000);
    std::chrono::milliseconds reconnect_interval(1000);

    server_manager_ = std::make_unique<local_datagram::server_manager>(socket_file_path,
                                                                       buffer_size,
                                                                       server_check_interval,
                                                                       reconnect_interval);

    server_manager_->bound.connect([this, socket_file_path] {
      if (auto uid = session::get_current_console_user_id()) {
        chown(socket_file_path.c_str(), *uid, 0);
      }
      chmod(socket_file_path.c_str(), 0600);

      if (auto m = weak_grabbable_state_queues_manager_.lock()) {
        m->clear();
      }
    });

    server_manager_->bind_failed.connect([](auto&& error_code) {
      logger::get_logger().error("receiver bind_failed");
    });

    server_manager_->received.connect([this](auto&& buffer) {
      if (auto type = types::find_operation_type(*buffer)) {
        switch (*type) {
          case operation_type::grabbable_state_changed:
            if (buffer->size() != sizeof(operation_type_grabbable_state_changed_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::grabbable_state_changed`.");
            } else {
              auto p = reinterpret_cast<operation_type_grabbable_state_changed_struct*>(&((*buffer)[0]));

              if (auto m = weak_grabbable_state_queues_manager_.lock()) {
                m->update_grabbable_state(p->grabbable_state);
              }
            }
            break;

          case operation_type::caps_lock_state_changed:
            if (buffer->size() != sizeof(operation_type_caps_lock_state_changed_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::caps_lock_state_changed`.");
            } else {
              auto p = reinterpret_cast<operation_type_caps_lock_state_changed_struct*>(&((*buffer)[0]));

              if (device_grabber_) {
                device_grabber_->async_set_caps_lock_state(p->state);
              }

              logger::get_logger().info("`caps_lock_state` is updated.");
            }
            break;

          case operation_type::connect_console_user_server:
            if (buffer->size() != sizeof(operation_type_connect_console_user_server_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::connect_console_user_server`.");
            } else {
              auto p = reinterpret_cast<operation_type_connect_console_user_server_struct*>(&((*buffer)[0]));

              // Ensure user_core_configuration_file_path is null-terminated string even if corrupted data is sent.
              p->user_core_configuration_file_path[sizeof(p->user_core_configuration_file_path) - 1] = '\0';
              std::string user_core_configuration_file_path(p->user_core_configuration_file_path);

              logger::get_logger().info("karabiner_console_user_server is connected (pid:{0})", p->pid);

              console_user_server_client_ = nullptr;
              console_user_server_client_ = std::make_shared<console_user_server_client>();

              console_user_server_client_->connected.connect([this, user_core_configuration_file_path] {
                stop_device_grabber();
                start_device_grabber(user_core_configuration_file_path);
              });

              console_user_server_client_->connect_failed.connect([this](auto&& error_code) {
                console_user_server_client_ = nullptr;

                stop_device_grabber();
                start_grabbing_if_system_core_configuration_file_exists();
              });

              console_user_server_client_->closed.connect([this] {
                console_user_server_client_ = nullptr;

                stop_device_grabber();
                start_grabbing_if_system_core_configuration_file_exists();
              });

              console_user_server_client_->async_start();
            }
            break;

          case operation_type::system_preferences_updated:
            if (buffer->size() < sizeof(operation_type_system_preferences_updated_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::system_preferences_updated`.");
            } else {
              auto p = reinterpret_cast<operation_type_system_preferences_updated_struct*>(&((*buffer)[0]));

              system_preferences_ = p->system_preferences;

              if (device_grabber_) {
                device_grabber_->async_set_system_preferences(p->system_preferences);
              }

              logger::get_logger().info("`system_preferences` is updated.");
            }
            break;

          case operation_type::frontmost_application_changed:
            if (buffer->size() < sizeof(operation_type_frontmost_application_changed_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::frontmost_application_changed`.");
            } else {
              auto p = reinterpret_cast<operation_type_frontmost_application_changed_struct*>(&((*buffer)[0]));

              // Ensure bundle_identifier and file_path are null-terminated string even if corrupted data is sent.
              p->bundle_identifier[sizeof(p->bundle_identifier) - 1] = '\0';
              p->file_path[sizeof(p->file_path) - 1] = '\0';

              frontmost_application_bundle_identifier_ = p->bundle_identifier;
              frontmost_application_file_path_ = p->file_path;

              if (device_grabber_) {
                device_grabber_->async_post_frontmost_application_changed_event(frontmost_application_bundle_identifier_,
                                                                                frontmost_application_file_path_);
              }
            }
            break;

          case operation_type::input_source_changed:
            if (buffer->size() < sizeof(operation_type_input_source_changed_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::input_source_changed`.");
            } else {
              auto p = reinterpret_cast<operation_type_input_source_changed_struct*>(&((*buffer)[0]));

              // Ensure bundle_identifier and file_path are null-terminated string even if corrupted data is sent.
              p->language[sizeof(p->language) - 1] = '\0';
              p->input_source_id[sizeof(p->input_source_id) - 1] = '\0';
              p->input_mode_id[sizeof(p->input_mode_id) - 1] = '\0';

              input_source_identifiers_ = input_source_identifiers(std::string(p->language),
                                                                   std::string(p->input_source_id),
                                                                   std::string(p->input_mode_id));

              if (device_grabber_) {
                device_grabber_->async_post_input_source_changed_event(input_source_identifiers_);
              }
            }
            break;

          default:
            break;
        }
      }
    });

    server_manager_->async_start();

    start_grabbing_if_system_core_configuration_file_exists();

    logger::get_logger().info("receiver is initialized");
  }

  virtual ~receiver(void) {
    detach_from_dispatcher([this] {
      server_manager_ = nullptr;
      console_user_server_client_ = nullptr;
      stop_device_grabber();
    });

    logger::get_logger().info("receiver is terminated");
  }

private:
  void start_grabbing_if_system_core_configuration_file_exists(void) {
    auto file_path = constants::get_system_core_configuration_file_path();
    if (filesystem::exists(file_path)) {
      stop_device_grabber();
      start_device_grabber(file_path);
    }
  }

  void start_device_grabber(const std::string& configuration_file_path) {
    if (device_grabber_) {
      return;
    }

    device_grabber_ = std::make_unique<device_grabber>(weak_grabbable_state_queues_manager_,
                                                       console_user_server_client_);

    device_grabber_->async_set_system_preferences(system_preferences_);
    device_grabber_->async_post_frontmost_application_changed_event(frontmost_application_bundle_identifier_,
                                                                    frontmost_application_file_path_);
    device_grabber_->async_post_input_source_changed_event(input_source_identifiers_);

    device_grabber_->async_start(configuration_file_path);

    logger::get_logger().info("device_grabber is started.");
  }

  void stop_device_grabber(void) {
    if (!device_grabber_) {
      return;
    }

    device_grabber_ = nullptr;

    logger::get_logger().info("device_grabber is stopped.");
  }

  std::weak_ptr<grabbable_state_queues_manager> weak_grabbable_state_queues_manager_;

  std::unique_ptr<local_datagram::server_manager> server_manager_;
  std::shared_ptr<console_user_server_client> console_user_server_client_;
  std::unique_ptr<device_grabber> device_grabber_;

  system_preferences system_preferences_;
  std::string frontmost_application_bundle_identifier_;
  std::string frontmost_application_file_path_;
  input_source_identifiers input_source_identifiers_;
};
} // namespace krbn
<commit_msg>remove a log message<commit_after>#pragma once

// `krbn::receiver` can be used safely in a multi-threaded environment.

#include "console_user_server_client.hpp"
#include "constants.hpp"
#include "device_grabber.hpp"
#include "dispatcher.hpp"
#include "grabbable_state_queues_manager.hpp"
#include "local_datagram/server_manager.hpp"
#include "session.hpp"
#include "types.hpp"
#include <vector>

namespace krbn {
class receiver final : public pqrs::dispatcher::extra::dispatcher_client {
public:
  receiver(const receiver&) = delete;

  receiver(std::weak_ptr<grabbable_state_queues_manager> weak_grabbable_state_queues_manager) : dispatcher_client(),
                                                                                weak_grabbable_state_queues_manager_(weak_grabbable_state_queues_manager) {
    std::string socket_file_path(constants::get_grabber_socket_file_path());

    unlink(socket_file_path.c_str());

    size_t buffer_size = 32 * 1024;
    std::chrono::milliseconds server_check_interval(3000);
    std::chrono::milliseconds reconnect_interval(1000);

    server_manager_ = std::make_unique<local_datagram::server_manager>(socket_file_path,
                                                                       buffer_size,
                                                                       server_check_interval,
                                                                       reconnect_interval);

    server_manager_->bound.connect([this, socket_file_path] {
      if (auto uid = session::get_current_console_user_id()) {
        chown(socket_file_path.c_str(), *uid, 0);
      }
      chmod(socket_file_path.c_str(), 0600);

      if (auto m = weak_grabbable_state_queues_manager_.lock()) {
        m->clear();
      }
    });

    server_manager_->bind_failed.connect([](auto&& error_code) {
      logger::get_logger().error("receiver bind_failed");
    });

    server_manager_->received.connect([this](auto&& buffer) {
      if (auto type = types::find_operation_type(*buffer)) {
        switch (*type) {
          case operation_type::grabbable_state_changed:
            if (buffer->size() != sizeof(operation_type_grabbable_state_changed_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::grabbable_state_changed`.");
            } else {
              auto p = reinterpret_cast<operation_type_grabbable_state_changed_struct*>(&((*buffer)[0]));

              if (auto m = weak_grabbable_state_queues_manager_.lock()) {
                m->update_grabbable_state(p->grabbable_state);
              }
            }
            break;

          case operation_type::caps_lock_state_changed:
            if (buffer->size() != sizeof(operation_type_caps_lock_state_changed_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::caps_lock_state_changed`.");
            } else {
              auto p = reinterpret_cast<operation_type_caps_lock_state_changed_struct*>(&((*buffer)[0]));

              if (device_grabber_) {
                device_grabber_->async_set_caps_lock_state(p->state);
              }
            }
            break;

          case operation_type::connect_console_user_server:
            if (buffer->size() != sizeof(operation_type_connect_console_user_server_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::connect_console_user_server`.");
            } else {
              auto p = reinterpret_cast<operation_type_connect_console_user_server_struct*>(&((*buffer)[0]));

              // Ensure user_core_configuration_file_path is null-terminated string even if corrupted data is sent.
              p->user_core_configuration_file_path[sizeof(p->user_core_configuration_file_path) - 1] = '\0';
              std::string user_core_configuration_file_path(p->user_core_configuration_file_path);

              logger::get_logger().info("karabiner_console_user_server is connected (pid:{0})", p->pid);

              console_user_server_client_ = nullptr;
              console_user_server_client_ = std::make_shared<console_user_server_client>();

              console_user_server_client_->connected.connect([this, user_core_configuration_file_path] {
                stop_device_grabber();
                start_device_grabber(user_core_configuration_file_path);
              });

              console_user_server_client_->connect_failed.connect([this](auto&& error_code) {
                console_user_server_client_ = nullptr;

                stop_device_grabber();
                start_grabbing_if_system_core_configuration_file_exists();
              });

              console_user_server_client_->closed.connect([this] {
                console_user_server_client_ = nullptr;

                stop_device_grabber();
                start_grabbing_if_system_core_configuration_file_exists();
              });

              console_user_server_client_->async_start();
            }
            break;

          case operation_type::system_preferences_updated:
            if (buffer->size() < sizeof(operation_type_system_preferences_updated_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::system_preferences_updated`.");
            } else {
              auto p = reinterpret_cast<operation_type_system_preferences_updated_struct*>(&((*buffer)[0]));

              system_preferences_ = p->system_preferences;

              if (device_grabber_) {
                device_grabber_->async_set_system_preferences(p->system_preferences);
              }

              logger::get_logger().info("`system_preferences` is updated.");
            }
            break;

          case operation_type::frontmost_application_changed:
            if (buffer->size() < sizeof(operation_type_frontmost_application_changed_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::frontmost_application_changed`.");
            } else {
              auto p = reinterpret_cast<operation_type_frontmost_application_changed_struct*>(&((*buffer)[0]));

              // Ensure bundle_identifier and file_path are null-terminated string even if corrupted data is sent.
              p->bundle_identifier[sizeof(p->bundle_identifier) - 1] = '\0';
              p->file_path[sizeof(p->file_path) - 1] = '\0';

              frontmost_application_bundle_identifier_ = p->bundle_identifier;
              frontmost_application_file_path_ = p->file_path;

              if (device_grabber_) {
                device_grabber_->async_post_frontmost_application_changed_event(frontmost_application_bundle_identifier_,
                                                                                frontmost_application_file_path_);
              }
            }
            break;

          case operation_type::input_source_changed:
            if (buffer->size() < sizeof(operation_type_input_source_changed_struct)) {
              logger::get_logger().error("Invalid size for `operation_type::input_source_changed`.");
            } else {
              auto p = reinterpret_cast<operation_type_input_source_changed_struct*>(&((*buffer)[0]));

              // Ensure bundle_identifier and file_path are null-terminated string even if corrupted data is sent.
              p->language[sizeof(p->language) - 1] = '\0';
              p->input_source_id[sizeof(p->input_source_id) - 1] = '\0';
              p->input_mode_id[sizeof(p->input_mode_id) - 1] = '\0';

              input_source_identifiers_ = input_source_identifiers(std::string(p->language),
                                                                   std::string(p->input_source_id),
                                                                   std::string(p->input_mode_id));

              if (device_grabber_) {
                device_grabber_->async_post_input_source_changed_event(input_source_identifiers_);
              }
            }
            break;

          default:
            break;
        }
      }
    });

    server_manager_->async_start();

    start_grabbing_if_system_core_configuration_file_exists();

    logger::get_logger().info("receiver is initialized");
  }

  virtual ~receiver(void) {
    detach_from_dispatcher([this] {
      server_manager_ = nullptr;
      console_user_server_client_ = nullptr;
      stop_device_grabber();
    });

    logger::get_logger().info("receiver is terminated");
  }

private:
  void start_grabbing_if_system_core_configuration_file_exists(void) {
    auto file_path = constants::get_system_core_configuration_file_path();
    if (filesystem::exists(file_path)) {
      stop_device_grabber();
      start_device_grabber(file_path);
    }
  }

  void start_device_grabber(const std::string& configuration_file_path) {
    if (device_grabber_) {
      return;
    }

    device_grabber_ = std::make_unique<device_grabber>(weak_grabbable_state_queues_manager_,
                                                       console_user_server_client_);

    device_grabber_->async_set_system_preferences(system_preferences_);
    device_grabber_->async_post_frontmost_application_changed_event(frontmost_application_bundle_identifier_,
                                                                    frontmost_application_file_path_);
    device_grabber_->async_post_input_source_changed_event(input_source_identifiers_);

    device_grabber_->async_start(configuration_file_path);

    logger::get_logger().info("device_grabber is started.");
  }

  void stop_device_grabber(void) {
    if (!device_grabber_) {
      return;
    }

    device_grabber_ = nullptr;

    logger::get_logger().info("device_grabber is stopped.");
  }

  std::weak_ptr<grabbable_state_queues_manager> weak_grabbable_state_queues_manager_;

  std::unique_ptr<local_datagram::server_manager> server_manager_;
  std::shared_ptr<console_user_server_client> console_user_server_client_;
  std::unique_ptr<device_grabber> device_grabber_;

  system_preferences system_preferences_;
  std::string frontmost_application_bundle_identifier_;
  std::string frontmost_application_file_path_;
  input_source_identifiers input_source_identifiers_;
};
} // namespace krbn
<|endoftext|>


<commit_before>/*
 * DesktopMainWindow.cpp
 *
 * Copyright (C) 2009-18 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "DesktopMainWindow.hpp"

#include <QToolBar>
#include <QWebChannel>

#include <boost/bind.hpp>
#include <boost/format.hpp>

#include <core/FileSerializer.hpp>

#include "DesktopOptions.hpp"
#include "DesktopSlotBinders.hpp"
#include "DesktopSessionLauncher.hpp"
#include "DockTileView.hpp"
#include "DesktopActivationOverlay.hpp"
#include "DesktopRCommandEvaluator.hpp"

using namespace rstudio::core;

namespace rstudio {
namespace desktop {

namespace {

#ifdef _WIN32

void CALLBACK onDialogStart(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
                            LONG idObject, LONG idChild,
                            DWORD dwEventThread, DWORD dwmsEventTime)
{
   ::BringWindowToTop(hwnd);
}

#endif

} // end anonymous namespace

MainWindow::MainWindow(QUrl url) :
      GwtWindow(false, false, QString(), url, nullptr),
      menuCallback_(this),
      gwtCallback_(this, this),
      pSessionLauncher_(nullptr),
      pCurrentSessionProcess_(nullptr)
{
   RCommandEvaluator::setMainWindow(this);
   pToolbar_->setVisible(false);

#ifdef _WIN32
   eventHook_ = NULL;
#endif

   // bind GWT callbacks
   auto* channel = webPage()->webChannel();
   channel->registerObject(QStringLiteral("desktop"), &gwtCallback_);
   channel->registerObject(QStringLiteral("desktopMenuCallback"), &menuCallback_);

   // Dummy menu bar to deal with the fact that
   // the real menu bar isn't ready until well
   // after startup.
#ifndef Q_OS_MAC
   auto* pMainMenuStub = new QMenuBar(this);
   pMainMenuStub->addMenu(QString::fromUtf8("File"));
   pMainMenuStub->addMenu(QString::fromUtf8("Edit"));
   pMainMenuStub->addMenu(QString::fromUtf8("Code"));
   pMainMenuStub->addMenu(QString::fromUtf8("View"));
   pMainMenuStub->addMenu(QString::fromUtf8("Plots"));
   pMainMenuStub->addMenu(QString::fromUtf8("Session"));
   pMainMenuStub->addMenu(QString::fromUtf8("Build"));
   pMainMenuStub->addMenu(QString::fromUtf8("Debug"));
   pMainMenuStub->addMenu(QString::fromUtf8("Profile"));
   pMainMenuStub->addMenu(QString::fromUtf8("Tools"));
   pMainMenuStub->addMenu(QString::fromUtf8("Help"));
   setMenuBar(pMainMenuStub);
#endif

   connect(&menuCallback_, SIGNAL(menuBarCompleted(QMenuBar*)),
           this, SLOT(setMenuBar(QMenuBar*)));
   connect(&menuCallback_, SIGNAL(commandInvoked(QString)),
           this, SLOT(invokeCommand(QString)));

   connect(&menuCallback_, SIGNAL(zoomActualSize()), this, SLOT(zoomActualSize()));
   connect(&menuCallback_, SIGNAL(zoomIn()), this, SLOT(zoomIn()));
   connect(&menuCallback_, SIGNAL(zoomOut()), this, SLOT(zoomOut()));

   connect(&gwtCallback_, SIGNAL(workbenchInitialized()),
           this, SIGNAL(firstWorkbenchInitialized()));
   connect(&gwtCallback_, SIGNAL(workbenchInitialized()),
           this, SLOT(onWorkbenchInitialized()));

   connect(webView(), SIGNAL(onCloseWindowShortcut()),
           this, SLOT(onCloseWindowShortcut()));

   connect(qApp, SIGNAL(commitDataRequest(QSessionManager&)),
           this, SLOT(commitDataRequest(QSessionManager&)),
           Qt::DirectConnection);

   setWindowIcon(QIcon(QString::fromUtf8(":/icons/RStudio.ico")));

   setWindowTitle(desktop::activation().editionName());

#ifdef Q_OS_MAC
   auto* pDefaultMenu = new QMenuBar(this);
   pDefaultMenu->addMenu(new WindowMenu());
#endif

   desktop::enableFullscreenMode(this, true);
}

QString MainWindow::getSumatraPdfExePath()
{
   return desktopInfo().getSumatraPdfExePath();
}

void MainWindow::launchSession(bool reload)
{
   Error error = pSessionLauncher_->launchNextSession(reload);
   if (error)
   {
      LOG_ERROR(error);

      showMessageBox(QMessageBox::Critical,
                     this,
                     desktop::activation().editionName(),
                     QString::fromUtf8("The R session failed to start."),
                     QString());

      quit();
   }
}

void MainWindow::launchRStudio(const std::vector<std::string> &args,
                               const std::string& initialDir)
{
    pAppLauncher_->launchRStudio(args, initialDir);
}

void MainWindow::onWorkbenchInitialized()
{
   //QTimer::singleShot(300, this, SLOT(resetMargins()));

   // reset state (in case this occurred in response to a manual reload
   // or reload for a new project context)
   quitConfirmed_ = false;
   geometrySaved_ = false;

   webPage()->runJavaScript(
            QStringLiteral("window.desktopHooks.getActiveProjectDir()"),
            [&](QVariant qProjectDir)
   {
      QString projectDir = qProjectDir.toString();

      if (projectDir.length() > 0)
      {
         setWindowTitle(tr("%1 - %2").arg(projectDir).arg(desktop::activation().editionName()));
         DockTileView::setLabel(projectDir);
      }
      else
      {
         setWindowTitle(desktop::activation().editionName());
         DockTileView::setLabel(QString());
      }

      avoidMoveCursorIfNecessary();
   });
}

void MainWindow::resetMargins()
{
   setContentsMargins(0, 0, 0, 0);
}

// this notification occurs when windows or X11 is shutting
// down -- in this case we want to be a good citizen and just
// exit right away so we notify the gwt callback that a legit
// quit and exit is on the way and we set the quitConfirmed_
// flag so no prompting occurs (note that source documents
// have already been auto-saved so will be restored next time
// the current project context is opened)
void MainWindow::commitDataRequest(QSessionManager &manager)
{
   gwtCallback_.setPendingQuit(PendingQuitAndExit);
   quitConfirmed_ = true;
}

void MainWindow::loadUrl(const QUrl& url)
{
   webView()->setBaseUrl(url);
   webView()->load(url);
}

void MainWindow::quit()
{
   RCommandEvaluator::setMainWindow(nullptr);
   quitConfirmed_ = true;
   close();
}

void MainWindow::invokeCommand(QString commandId)
{
#ifdef Q_OS_MAC
   QString fmt = QStringLiteral(R"EOF(
var wnd;
try {
   wnd = window.$RStudio.last_focused_window;
} catch (e) {
   wnd = window;
}
wnd.desktopHooks.invokeCommand('%1');
)EOF");
#else
   QString fmt = QStringLiteral("window.desktopHooks.invokeCommand('%1')");
#endif

   QString command = fmt.arg(commandId);
   webPage()->runJavaScript(command);
}

namespace {

void closeAllSatellites(QWidget* mainWindow)
{
   for (auto window: QApplication::topLevelWidgets())
      if (window != mainWindow)
         window->close();
}

} // end anonymous namespace

void MainWindow::closeEvent(QCloseEvent* pEvent)
{
#ifdef _WIN32
   if (eventHook_)
      ::UnhookWinEvent(eventHook_);
#endif

   if (!geometrySaved_)
   {
      desktop::options().saveMainWindowBounds(this);
      geometrySaved_ = true;
   }

   if (quitConfirmed_ ||
       pCurrentSessionProcess_ == nullptr ||
       pCurrentSessionProcess_->state() != QProcess::Running)
   {
      closeAllSatellites(this);
      pEvent->accept();
      return;
   }

   pEvent->ignore();
   webPage()->runJavaScript(
            QStringLiteral("!!window.desktopHooks"),
            [&](QVariant hasQuitR) {

      if (!hasQuitR.toBool())
      {
         LOG_ERROR_MESSAGE("Main window closed unexpectedly");

         // exit to avoid user having to kill/force-close the application
         closeAllSatellites(this);
         QApplication::quit();
      }
      else
      {
         webPage()->runJavaScript(
                  QStringLiteral("window.desktopHooks.quitR()"),
                  [&](QVariant ignored)
         {
            // don't close all the satellites here since the user hasn't confirmed quit yet
         });
      }
   });
}

void MainWindow::setMenuBar(QMenuBar *pMenubar)
{
   delete menuBar();
   this->QMainWindow::setMenuBar(pMenubar);
}

void MainWindow::openFileInRStudio(QString path)
{
   QFileInfo fileInfo(path);
   if (!fileInfo.isAbsolute() || !fileInfo.exists() || !fileInfo.isFile())
      return;

   path = path.replace(QString::fromUtf8("\\"), QString::fromUtf8("\\\\"))
         .replace(QString::fromUtf8("\""), QString::fromUtf8("\\\""))
         .replace(QString::fromUtf8("\n"), QString::fromUtf8("\\n"));

   webView()->page()->runJavaScript(
            QString::fromUtf8("window.desktopHooks.openFile(\"") + path + QString::fromUtf8("\")"));
}

void MainWindow::onPdfViewerClosed(QString pdfPath)
{
   webView()->page()->runJavaScript(
            QString::fromUtf8("window.synctexNotifyPdfViewerClosed(\"") +
            pdfPath + QString::fromUtf8("\")"));
}

void MainWindow::onPdfViewerSyncSource(QString srcFile, int line, int column)
{
   boost::format fmt("window.desktopSynctexInverseSearch(\"%1%\", %2%, %3%)");
   std::string js = boost::str(fmt % srcFile.toStdString() % line % column);
   webView()->page()->runJavaScript(QString::fromStdString(js));
}

void MainWindow::onLicenseLost(QString licenseMessage)
{
   webView()->page()->runJavaScript(
            QString::fromUtf8("window.desktopHooks.licenseLost('") + licenseMessage +
            QString::fromUtf8("');"));
}

void MainWindow::onUpdateLicenseWarningBar(QString message)
{
   webView()->page()->runJavaScript(
            QString::fromUtf8("window.desktopHooks.updateLicenseWarningBar('") + message +
            QString::fromUtf8("');"));
}

// private interface for SessionLauncher

void MainWindow::setSessionLauncher(SessionLauncher* pSessionLauncher)
{
   pSessionLauncher_ = pSessionLauncher;
}

void MainWindow::setSessionProcess(QProcess* pSessionProcess)
{
   pCurrentSessionProcess_ = pSessionProcess;

   // when R creates dialogs (e.g. through utils::askYesNo), their first
   // invocation might show behind the RStudio window. this allows us
   // to detect when those Windows are opened and focused, and raise them
   // to the front.
#ifdef _WIN32
   if (eventHook_)
      ::UnhookWinEvent(eventHook_);

   if (pSessionProcess)
   {
      eventHook_ = ::SetWinEventHook(
               EVENT_SYSTEM_DIALOGSTART, EVENT_SYSTEM_DIALOGSTART,
               NULL,
               onDialogStart,
               pSessionProcess->processId(),
               0,
               WINEVENT_OUTOFCONTEXT);
   }
#endif

}

void MainWindow::setAppLauncher(ApplicationLaunch *pAppLauncher)
{
    pAppLauncher_ = pAppLauncher;
}

// allow SessionLauncher to collect restart requests from GwtCallback
int MainWindow::collectPendingQuitRequest()
{
   return gwtCallback_.collectPendingQuitRequest();
}

bool MainWindow::desktopHooksAvailable()
{
   return desktopInfo().desktopHooksAvailable();
}

void MainWindow::onActivated()
{
}

} // namespace desktop
} // namespace rstudio
<commit_msg>guard against null window<commit_after>/*
 * DesktopMainWindow.cpp
 *
 * Copyright (C) 2009-18 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "DesktopMainWindow.hpp"

#include <QToolBar>
#include <QWebChannel>

#include <boost/bind.hpp>
#include <boost/format.hpp>

#include <core/FileSerializer.hpp>

#include "DesktopOptions.hpp"
#include "DesktopSlotBinders.hpp"
#include "DesktopSessionLauncher.hpp"
#include "DockTileView.hpp"
#include "DesktopActivationOverlay.hpp"
#include "DesktopRCommandEvaluator.hpp"

using namespace rstudio::core;

namespace rstudio {
namespace desktop {

namespace {

#ifdef _WIN32

void CALLBACK onDialogStart(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
                            LONG idObject, LONG idChild,
                            DWORD dwEventThread, DWORD dwmsEventTime)
{
   ::BringWindowToTop(hwnd);
}

#endif

} // end anonymous namespace

MainWindow::MainWindow(QUrl url) :
      GwtWindow(false, false, QString(), url, nullptr),
      menuCallback_(this),
      gwtCallback_(this, this),
      pSessionLauncher_(nullptr),
      pCurrentSessionProcess_(nullptr)
{
   RCommandEvaluator::setMainWindow(this);
   pToolbar_->setVisible(false);

#ifdef _WIN32
   eventHook_ = NULL;
#endif

   // bind GWT callbacks
   auto* channel = webPage()->webChannel();
   channel->registerObject(QStringLiteral("desktop"), &gwtCallback_);
   channel->registerObject(QStringLiteral("desktopMenuCallback"), &menuCallback_);

   // Dummy menu bar to deal with the fact that
   // the real menu bar isn't ready until well
   // after startup.
#ifndef Q_OS_MAC
   auto* pMainMenuStub = new QMenuBar(this);
   pMainMenuStub->addMenu(QString::fromUtf8("File"));
   pMainMenuStub->addMenu(QString::fromUtf8("Edit"));
   pMainMenuStub->addMenu(QString::fromUtf8("Code"));
   pMainMenuStub->addMenu(QString::fromUtf8("View"));
   pMainMenuStub->addMenu(QString::fromUtf8("Plots"));
   pMainMenuStub->addMenu(QString::fromUtf8("Session"));
   pMainMenuStub->addMenu(QString::fromUtf8("Build"));
   pMainMenuStub->addMenu(QString::fromUtf8("Debug"));
   pMainMenuStub->addMenu(QString::fromUtf8("Profile"));
   pMainMenuStub->addMenu(QString::fromUtf8("Tools"));
   pMainMenuStub->addMenu(QString::fromUtf8("Help"));
   setMenuBar(pMainMenuStub);
#endif

   connect(&menuCallback_, SIGNAL(menuBarCompleted(QMenuBar*)),
           this, SLOT(setMenuBar(QMenuBar*)));
   connect(&menuCallback_, SIGNAL(commandInvoked(QString)),
           this, SLOT(invokeCommand(QString)));

   connect(&menuCallback_, SIGNAL(zoomActualSize()), this, SLOT(zoomActualSize()));
   connect(&menuCallback_, SIGNAL(zoomIn()), this, SLOT(zoomIn()));
   connect(&menuCallback_, SIGNAL(zoomOut()), this, SLOT(zoomOut()));

   connect(&gwtCallback_, SIGNAL(workbenchInitialized()),
           this, SIGNAL(firstWorkbenchInitialized()));
   connect(&gwtCallback_, SIGNAL(workbenchInitialized()),
           this, SLOT(onWorkbenchInitialized()));

   connect(webView(), SIGNAL(onCloseWindowShortcut()),
           this, SLOT(onCloseWindowShortcut()));

   connect(qApp, SIGNAL(commitDataRequest(QSessionManager&)),
           this, SLOT(commitDataRequest(QSessionManager&)),
           Qt::DirectConnection);

   setWindowIcon(QIcon(QString::fromUtf8(":/icons/RStudio.ico")));

   setWindowTitle(desktop::activation().editionName());

#ifdef Q_OS_MAC
   auto* pDefaultMenu = new QMenuBar(this);
   pDefaultMenu->addMenu(new WindowMenu());
#endif

   desktop::enableFullscreenMode(this, true);
}

QString MainWindow::getSumatraPdfExePath()
{
   return desktopInfo().getSumatraPdfExePath();
}

void MainWindow::launchSession(bool reload)
{
   Error error = pSessionLauncher_->launchNextSession(reload);
   if (error)
   {
      LOG_ERROR(error);

      showMessageBox(QMessageBox::Critical,
                     this,
                     desktop::activation().editionName(),
                     QString::fromUtf8("The R session failed to start."),
                     QString());

      quit();
   }
}

void MainWindow::launchRStudio(const std::vector<std::string> &args,
                               const std::string& initialDir)
{
    pAppLauncher_->launchRStudio(args, initialDir);
}

void MainWindow::onWorkbenchInitialized()
{
   //QTimer::singleShot(300, this, SLOT(resetMargins()));

   // reset state (in case this occurred in response to a manual reload
   // or reload for a new project context)
   quitConfirmed_ = false;
   geometrySaved_ = false;

   webPage()->runJavaScript(
            QStringLiteral("window.desktopHooks.getActiveProjectDir()"),
            [&](QVariant qProjectDir)
   {
      QString projectDir = qProjectDir.toString();

      if (projectDir.length() > 0)
      {
         setWindowTitle(tr("%1 - %2").arg(projectDir).arg(desktop::activation().editionName()));
         DockTileView::setLabel(projectDir);
      }
      else
      {
         setWindowTitle(desktop::activation().editionName());
         DockTileView::setLabel(QString());
      }

      avoidMoveCursorIfNecessary();
   });
}

void MainWindow::resetMargins()
{
   setContentsMargins(0, 0, 0, 0);
}

// this notification occurs when windows or X11 is shutting
// down -- in this case we want to be a good citizen and just
// exit right away so we notify the gwt callback that a legit
// quit and exit is on the way and we set the quitConfirmed_
// flag so no prompting occurs (note that source documents
// have already been auto-saved so will be restored next time
// the current project context is opened)
void MainWindow::commitDataRequest(QSessionManager &manager)
{
   gwtCallback_.setPendingQuit(PendingQuitAndExit);
   quitConfirmed_ = true;
}

void MainWindow::loadUrl(const QUrl& url)
{
   webView()->setBaseUrl(url);
   webView()->load(url);
}

void MainWindow::quit()
{
   RCommandEvaluator::setMainWindow(nullptr);
   quitConfirmed_ = true;
   close();
}

void MainWindow::invokeCommand(QString commandId)
{
#ifdef Q_OS_MAC
   QString fmt = QStringLiteral(R"EOF(
var wnd;
try {
   wnd = window.$RStudio.last_focused_window;
} catch (e) {
   wnd = window;
}
(wnd || window).desktopHooks.invokeCommand('%1');
)EOF");
#else
   QString fmt = QStringLiteral("window.desktopHooks.invokeCommand('%1')");
#endif

   QString command = fmt.arg(commandId);
   webPage()->runJavaScript(command);
}

namespace {

void closeAllSatellites(QWidget* mainWindow)
{
   for (auto window: QApplication::topLevelWidgets())
      if (window != mainWindow)
         window->close();
}

} // end anonymous namespace

void MainWindow::closeEvent(QCloseEvent* pEvent)
{
#ifdef _WIN32
   if (eventHook_)
      ::UnhookWinEvent(eventHook_);
#endif

   if (!geometrySaved_)
   {
      desktop::options().saveMainWindowBounds(this);
      geometrySaved_ = true;
   }

   if (quitConfirmed_ ||
       pCurrentSessionProcess_ == nullptr ||
       pCurrentSessionProcess_->state() != QProcess::Running)
   {
      closeAllSatellites(this);
      pEvent->accept();
      return;
   }

   pEvent->ignore();
   webPage()->runJavaScript(
            QStringLiteral("!!window.desktopHooks"),
            [&](QVariant hasQuitR) {

      if (!hasQuitR.toBool())
      {
         LOG_ERROR_MESSAGE("Main window closed unexpectedly");

         // exit to avoid user having to kill/force-close the application
         closeAllSatellites(this);
         QApplication::quit();
      }
      else
      {
         webPage()->runJavaScript(
                  QStringLiteral("window.desktopHooks.quitR()"),
                  [&](QVariant ignored)
         {
            // don't close all the satellites here since the user hasn't confirmed quit yet
         });
      }
   });
}

void MainWindow::setMenuBar(QMenuBar *pMenubar)
{
   delete menuBar();
   this->QMainWindow::setMenuBar(pMenubar);
}

void MainWindow::openFileInRStudio(QString path)
{
   QFileInfo fileInfo(path);
   if (!fileInfo.isAbsolute() || !fileInfo.exists() || !fileInfo.isFile())
      return;

   path = path.replace(QString::fromUtf8("\\"), QString::fromUtf8("\\\\"))
         .replace(QString::fromUtf8("\""), QString::fromUtf8("\\\""))
         .replace(QString::fromUtf8("\n"), QString::fromUtf8("\\n"));

   webView()->page()->runJavaScript(
            QString::fromUtf8("window.desktopHooks.openFile(\"") + path + QString::fromUtf8("\")"));
}

void MainWindow::onPdfViewerClosed(QString pdfPath)
{
   webView()->page()->runJavaScript(
            QString::fromUtf8("window.synctexNotifyPdfViewerClosed(\"") +
            pdfPath + QString::fromUtf8("\")"));
}

void MainWindow::onPdfViewerSyncSource(QString srcFile, int line, int column)
{
   boost::format fmt("window.desktopSynctexInverseSearch(\"%1%\", %2%, %3%)");
   std::string js = boost::str(fmt % srcFile.toStdString() % line % column);
   webView()->page()->runJavaScript(QString::fromStdString(js));
}

void MainWindow::onLicenseLost(QString licenseMessage)
{
   webView()->page()->runJavaScript(
            QString::fromUtf8("window.desktopHooks.licenseLost('") + licenseMessage +
            QString::fromUtf8("');"));
}

void MainWindow::onUpdateLicenseWarningBar(QString message)
{
   webView()->page()->runJavaScript(
            QString::fromUtf8("window.desktopHooks.updateLicenseWarningBar('") + message +
            QString::fromUtf8("');"));
}

// private interface for SessionLauncher

void MainWindow::setSessionLauncher(SessionLauncher* pSessionLauncher)
{
   pSessionLauncher_ = pSessionLauncher;
}

void MainWindow::setSessionProcess(QProcess* pSessionProcess)
{
   pCurrentSessionProcess_ = pSessionProcess;

   // when R creates dialogs (e.g. through utils::askYesNo), their first
   // invocation might show behind the RStudio window. this allows us
   // to detect when those Windows are opened and focused, and raise them
   // to the front.
#ifdef _WIN32
   if (eventHook_)
      ::UnhookWinEvent(eventHook_);

   if (pSessionProcess)
   {
      eventHook_ = ::SetWinEventHook(
               EVENT_SYSTEM_DIALOGSTART, EVENT_SYSTEM_DIALOGSTART,
               NULL,
               onDialogStart,
               pSessionProcess->processId(),
               0,
               WINEVENT_OUTOFCONTEXT);
   }
#endif

}

void MainWindow::setAppLauncher(ApplicationLaunch *pAppLauncher)
{
    pAppLauncher_ = pAppLauncher;
}

// allow SessionLauncher to collect restart requests from GwtCallback
int MainWindow::collectPendingQuitRequest()
{
   return gwtCallback_.collectPendingQuitRequest();
}

bool MainWindow::desktopHooksAvailable()
{
   return desktopInfo().desktopHooksAvailable();
}

void MainWindow::onActivated()
{
}

} // namespace desktop
} // namespace rstudio
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the either Technology Preview License Agreement or the
** Beta Release License Agreement.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtCore/qdebug.h>
#include <QtCore/qstack.h>
#include <QXmlStreamReader>
#include <private/qmlcustomparser_p.h>
#include <private/qmlparser_p.h>
#include "qmlopenmetaobject.h"
#include <qmlcontext.h>
#include "qmllistmodel.h"

Q_DECLARE_METATYPE(QListModelInterface *)

QT_BEGIN_NAMESPACE

#define DATA_ROLE_ID 1
#define DATA_ROLE_NAME "data"

struct ListInstruction
{
    enum { Push, Pop, Value, Set } type;
    int dataIdx;
};

struct ListModelData
{
    int dataOffset;
    int instrCount;
    ListInstruction *instructions() const { return (ListInstruction *)((char *)this + sizeof(ListModelData)); }
};

/*!
    \qmlclass ListModel
    \brief The ListModel element defines a free-form list data source.

    The ListModel is a simple hierarchy of items containing data roles.
    For example:

    \code
    ListModel {
        id: FruitModel
        ListElement {
            name: "Apple"
            cost: 2.45
        }
        ListElement {
            name: "Orange"
            cost: 3.25
        }
        ListElement {
            name: "Banana"
            cost: 1.95
        }
    }
    \endcode

    Item roles (properties) must begin with a lower-case letter.  The above example defines a
    ListModel containing three items, with the roles "name" and "cost".

    The defined model can be used in views such as ListView:
    \code
    Component {
        id: FruitDelegate
        Item {
            width: 200; height: 50
            Text { text: name }
            Text { text: '$'+cost; anchors.right: parent.right }
        }
    }

    ListView {
        model: FruitModel
        delegate: FruitDelegate
        anchors.fill: parent
    }
    \endcode

    It is possible for roles to contain list data.  In the example below we create a list of fruit attributes:

    \code
    ListModel {
        id: FruitModel
        ListElement {
            name: "Apple"
            cost: 2.45
            attributes: [
                ListElement { description: "Core" },
                ListElement { description: "Deciduous" }
            ]
        }
        ListElement {
            name: "Orange"
            cost: 3.25
            attributes: [
                ListElement { description: "Citrus" }
            ]
        }
        ListElement {
            name: "Banana"
            cost: 1.95
            attributes: [
                ListElement { description: "Tropical" }
                ListElement { description: "Seedless" }
            ]
        }
    }
    \endcode

    The delegate below will list all the fruit attributes:
    \code
    Component {
        id: FruitDelegate
        Item {
            width: 200; height: 50
            Text { id: Name; text: name }
            Text { text: '$'+cost; anchors.right: parent.right }
            HorizontalPositioner {
                anchors.top: Name.bottom
                spacing: 5
                Text { text: "Attributes:" }
                Repeater {
                    dataSource: attributes
                    Component { Text { text: description } }
                }
            }
        }
    }
    \endcode

*/

class ModelObject : public QObject
{
    Q_OBJECT
public:
    ModelObject(ModelNode *);

    void setValue(const QByteArray &name, const QVariant &val)
    {
        _mo->setValue(name, val);
    }

private:
    ModelNode *_node;
    bool _haveProperties;
    QmlOpenMetaObject *_mo;
};

struct ModelNode
{
    ModelNode();
    ~ModelNode();
    QString className;

    QList<QVariant> values;
    QHash<QString, ModelNode *> properties;

    QmlListModel *model() {
        if (!modelCache) {
            modelCache = new QmlListModel;
            modelCache->_root = this;
        }
        return modelCache;
    }

    ModelObject *object() {
        if (!objectCache) {
            objectCache = new ModelObject(this);
            QHash<QString, ModelNode *>::iterator it;
            for (it = properties.begin(); it != properties.end(); ++it) {
                if (!(*it)->values.isEmpty())
                    objectCache->setValue(it.key().toLatin1(), (*it)->values.first());
            }
        }
        return objectCache;
    }

    QmlListModel *modelCache;
    ModelObject *objectCache;
};

ModelObject::ModelObject(ModelNode *node)
: _node(node), _haveProperties(false), _mo(new QmlOpenMetaObject(this))
{
}

QmlListModel::QmlListModel(QObject *parent)
: QListModelInterface(parent), _rolesOk(false), _root(0)
{
}

QmlListModel::~QmlListModel()
{
}

void QmlListModel::checkRoles() const
{
    if (_rolesOk)
        return;

    for (int ii = 0; ii < _root->values.count(); ++ii) {
        ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(ii));
        if (node) {
            foreach (const QString &role, node->properties.keys())
                addRole(role);
        }
    }

    _rolesOk = true;
}

void QmlListModel::addRole(const QString &role) const
{
    if (!roleStrings.contains(role))
        roleStrings << role;
}

QList<int> QmlListModel::roles() const
{
    checkRoles();
    QList<int> rv;
    for (int ii = 0; ii < roleStrings.count(); ++ii)
        rv << ii;
    return rv;
}

QString QmlListModel::toString(int role) const
{
    checkRoles();
    if (role < roleStrings.count())
        return roleStrings.at(role);
    else
        return QString();
}

QVariant QmlListModel::valueForNode(ModelNode *node) const
{
    QObject *rv = 0;

    if (!node->properties.isEmpty()) {
        // Object
        rv = node->object();
    } else if (node->values.count() == 0) {
        // Invalid
        return QVariant();
    } else if (node->values.count() == 1) {
        // Value
        QVariant &var = node->values[0];
        ModelNode *valueNode = qvariant_cast<ModelNode *>(var);
        if (valueNode) {
            if (!valueNode->properties.isEmpty())
                rv = valueNode->object();
            else
                rv = valueNode->model();
        } else {
            return var;
        }
    } else if (node->values.count() > 1) {
        // List
        rv = node->model();
    }

    if (rv)
        return QVariant::fromValue(rv);
    else
        return QVariant();
}

QHash<int,QVariant> QmlListModel::data(int index, const QList<int> &roles) const
{
    checkRoles();
    QHash<int, QVariant> rv;
    if (index >= count())
        return rv;

    ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
    if (!node)
        return rv;

    for (int ii = 0; ii < roles.count(); ++ii) {
        const QString &roleString = roleStrings.at(roles.at(ii));

        QHash<QString, ModelNode *>::ConstIterator iter =
            node->properties.find(roleString);
        if (iter != node->properties.end()) {
            ModelNode *row = *iter;
            rv.insert(roles.at(ii), valueForNode(row));
        }
    }

    return rv;
}

/*!
    \qmlproperty int ListModel::count
    The number of data entries in the model.
*/
int QmlListModel::count() const
{
    if (!_root) return 0;
    return _root->values.count();
}

class QmlListModelParser : public QmlCustomParser
{
public:
    QByteArray compile(const QList<QmlCustomParserProperty> &, bool *ok);
    bool compileProperty(const QmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data);
    void setCustomData(QObject *, const QByteArray &);
};

bool QmlListModelParser::compileProperty(const QmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data)
{
    QList<QVariant> values = prop.assignedValues();
    for(int ii = 0; ii < values.count(); ++ii) {
        const QVariant &value = values.at(ii);

        if(value.userType() == qMetaTypeId<QmlCustomParserNode>()) {
            QmlCustomParserNode node =
                qvariant_cast<QmlCustomParserNode>(value);

            {
            ListInstruction li;
            li.type = ListInstruction::Push;
            li.dataIdx = -1;
            instr << li;
            }

            QList<QmlCustomParserProperty> props = node.properties();
            for(int jj = 0; jj < props.count(); ++jj) {
                const QmlCustomParserProperty &nodeProp = props.at(jj);
                if(nodeProp.name() == "")
                    return false;

                ListInstruction li;
                int ref = data.count();
                data.append(nodeProp.name());
                data.append('\0');
                li.type = ListInstruction::Set;
                li.dataIdx = ref;
                instr << li;

                if(!compileProperty(nodeProp, instr, data))
                    return false;

                li.type = ListInstruction::Pop;
                li.dataIdx = -1;
                instr << li;
            }

            {
            ListInstruction li;
            li.type = ListInstruction::Pop;
            li.dataIdx = -1;
            instr << li;
            }

        } else {

            QmlParser::Variant variant =
                qvariant_cast<QmlParser::Variant>(value);

            int ref = data.count();
            QByteArray d = variant.asScript().toLatin1();
            d.append('\0');
            data.append(d);

            ListInstruction li;
            li.type = ListInstruction::Value;
            li.dataIdx = ref;
            instr << li;

        }
    }

    return true;
}

QByteArray QmlListModelParser::compile(const QList<QmlCustomParserProperty> &customProps, bool *ok)
{
    *ok = true;
    QList<ListInstruction> instr;
    QByteArray data;

    for(int ii = 0; ii < customProps.count(); ++ii) {
        const QmlCustomParserProperty &prop = customProps.at(ii);
        if(prop.name() != "") { // isn't default property
            *ok = false;
            return QByteArray();
        }

        if(!compileProperty(prop, instr, data)) {
            *ok = false;
            return QByteArray();
        }
    }

    int size = sizeof(ListModelData) +
               instr.count() * sizeof(ListInstruction) +
               data.count();

    QByteArray rv;
    rv.resize(size);

    ListModelData *lmd = (ListModelData *)rv.data();
    lmd->dataOffset = sizeof(ListModelData) +
                     instr.count() * sizeof(ListInstruction);
    lmd->instrCount = instr.count();
    for (int ii = 0; ii < instr.count(); ++ii)
        lmd->instructions()[ii] = instr.at(ii);
    ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count());

    return rv;
}

void QmlListModelParser::setCustomData(QObject *obj, const QByteArray &d)
{
    QmlListModel *rv = static_cast<QmlListModel *>(obj);

    ModelNode *root = new ModelNode;
    rv->_root = root;
    QStack<ModelNode *> nodes;
    nodes << root;

    const ListModelData *lmd = (const ListModelData *)d.constData();
    const char *data = ((const char *)lmd) + lmd->dataOffset;

    for (int ii = 0; ii < lmd->instrCount; ++ii) {
        const ListInstruction &instr = lmd->instructions()[ii];

        switch(instr.type) {
        case ListInstruction::Push:
            {
                ModelNode *n = nodes.top();
                ModelNode *n2 = new ModelNode;
                n->values << qVariantFromValue(n2);
                nodes.push(n2);
            }
            break;

        case ListInstruction::Pop:
            nodes.pop();
            break;

        case ListInstruction::Value:
            {
                ModelNode *n = nodes.top();
                n->values.append(QByteArray(data + instr.dataIdx));
            }
            break;

        case ListInstruction::Set:
            {
                ModelNode *n = nodes.top();
                ModelNode *n2 = new ModelNode;
                n->properties.insert(QLatin1String(data + instr.dataIdx), n2);
                nodes.push(n2);
            }
            break;
        }
    }
}

QML_DEFINE_CUSTOM_TYPE(Qt, 4,6, (QT_VERSION&0x00ff00)>>8, ListModel, QmlListModel, QmlListModelParser)

// ### FIXME
class QmlListElement : public QObject
{
Q_OBJECT
};
QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ListElement,QmlListElement)

static void dump(ModelNode *node, int ind)
{
    QByteArray indentBa(ind * 4, ' ');
    const char *indent = indentBa.constData();

    for (int ii = 0; ii < node->values.count(); ++ii) {
        ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii));
        if (subNode) {
            qWarning().nospace() << indent << "Sub-node " << ii << ": class " << subNode->className;
            dump(subNode, ind + 1);
        } else {
            qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString();
        }
    }

    for (QHash<QString, ModelNode *>::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) {
        qWarning().nospace() << indent << "Property " << iter.key() << ":";
        dump(iter.value(), ind + 1);
    }
}

ModelNode::ModelNode()
: modelCache(0), objectCache(0)
{
}

ModelNode::~ModelNode()
{
    qDeleteAll(properties);
    for (int ii = 0; ii < values.count(); ++ii) {
        ModelNode *node = qvariant_cast<ModelNode *>(values.at(ii));
        if (node) { delete node; node = 0; }
    }
    if (modelCache) { delete modelCache; modelCache = 0; }
}

QT_END_NAMESPACE

Q_DECLARE_METATYPE(ModelNode *)
QML_DECLARE_TYPE(QmlListElement)

#include "qmllistmodel.moc"
<commit_msg>Doc tweak.<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the either Technology Preview License Agreement or the
** Beta Release License Agreement.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtCore/qdebug.h>
#include <QtCore/qstack.h>
#include <QXmlStreamReader>
#include <private/qmlcustomparser_p.h>
#include <private/qmlparser_p.h>
#include "qmlopenmetaobject.h"
#include <qmlcontext.h>
#include "qmllistmodel.h"

Q_DECLARE_METATYPE(QListModelInterface *)

QT_BEGIN_NAMESPACE

#define DATA_ROLE_ID 1
#define DATA_ROLE_NAME "data"

struct ListInstruction
{
    enum { Push, Pop, Value, Set } type;
    int dataIdx;
};

struct ListModelData
{
    int dataOffset;
    int instrCount;
    ListInstruction *instructions() const { return (ListInstruction *)((char *)this + sizeof(ListModelData)); }
};

/*!
    \qmlclass ListModel
    \brief The ListModel element defines a free-form list data source.

    The ListModel is a simple hierarchy of elements containing data roles.
    For example:

    \code
    ListModel {
        id: FruitModel
        ListElement {
            name: "Apple"
            cost: 2.45
        }
        ListElement {
            name: "Orange"
            cost: 3.25
        }
        ListElement {
            name: "Banana"
            cost: 1.95
        }
    }
    \endcode

    Roles (properties) must begin with a lower-case letter.  The above example defines a
    ListModel containing three elements, with the roles "name" and "cost".

    The defined model can be used in views such as ListView:
    \code
    Component {
        id: FruitDelegate
        Item {
            width: 200; height: 50
            Text { text: name }
            Text { text: '$'+cost; anchors.right: parent.right }
        }
    }

    ListView {
        model: FruitModel
        delegate: FruitDelegate
        anchors.fill: parent
    }
    \endcode

    It is possible for roles to contain list data.  In the example below we create a list of fruit attributes:

    \code
    ListModel {
        id: FruitModel
        ListElement {
            name: "Apple"
            cost: 2.45
            attributes: [
                ListElement { description: "Core" },
                ListElement { description: "Deciduous" }
            ]
        }
        ListElement {
            name: "Orange"
            cost: 3.25
            attributes: [
                ListElement { description: "Citrus" }
            ]
        }
        ListElement {
            name: "Banana"
            cost: 1.95
            attributes: [
                ListElement { description: "Tropical" }
                ListElement { description: "Seedless" }
            ]
        }
    }
    \endcode

    The delegate below will list all the fruit attributes:
    \code
    Component {
        id: FruitDelegate
        Item {
            width: 200; height: 50
            Text { id: Name; text: name }
            Text { text: '$'+cost; anchors.right: parent.right }
            HorizontalPositioner {
                anchors.top: Name.bottom
                spacing: 5
                Text { text: "Attributes:" }
                Repeater {
                    dataSource: attributes
                    Component { Text { text: description } }
                }
            }
        }
    }
    \endcode

*/

class ModelObject : public QObject
{
    Q_OBJECT
public:
    ModelObject(ModelNode *);

    void setValue(const QByteArray &name, const QVariant &val)
    {
        _mo->setValue(name, val);
    }

private:
    ModelNode *_node;
    bool _haveProperties;
    QmlOpenMetaObject *_mo;
};

struct ModelNode
{
    ModelNode();
    ~ModelNode();
    QString className;

    QList<QVariant> values;
    QHash<QString, ModelNode *> properties;

    QmlListModel *model() {
        if (!modelCache) {
            modelCache = new QmlListModel;
            modelCache->_root = this;
        }
        return modelCache;
    }

    ModelObject *object() {
        if (!objectCache) {
            objectCache = new ModelObject(this);
            QHash<QString, ModelNode *>::iterator it;
            for (it = properties.begin(); it != properties.end(); ++it) {
                if (!(*it)->values.isEmpty())
                    objectCache->setValue(it.key().toLatin1(), (*it)->values.first());
            }
        }
        return objectCache;
    }

    QmlListModel *modelCache;
    ModelObject *objectCache;
};

ModelObject::ModelObject(ModelNode *node)
: _node(node), _haveProperties(false), _mo(new QmlOpenMetaObject(this))
{
}

QmlListModel::QmlListModel(QObject *parent)
: QListModelInterface(parent), _rolesOk(false), _root(0)
{
}

QmlListModel::~QmlListModel()
{
}

void QmlListModel::checkRoles() const
{
    if (_rolesOk)
        return;

    for (int ii = 0; ii < _root->values.count(); ++ii) {
        ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(ii));
        if (node) {
            foreach (const QString &role, node->properties.keys())
                addRole(role);
        }
    }

    _rolesOk = true;
}

void QmlListModel::addRole(const QString &role) const
{
    if (!roleStrings.contains(role))
        roleStrings << role;
}

QList<int> QmlListModel::roles() const
{
    checkRoles();
    QList<int> rv;
    for (int ii = 0; ii < roleStrings.count(); ++ii)
        rv << ii;
    return rv;
}

QString QmlListModel::toString(int role) const
{
    checkRoles();
    if (role < roleStrings.count())
        return roleStrings.at(role);
    else
        return QString();
}

QVariant QmlListModel::valueForNode(ModelNode *node) const
{
    QObject *rv = 0;

    if (!node->properties.isEmpty()) {
        // Object
        rv = node->object();
    } else if (node->values.count() == 0) {
        // Invalid
        return QVariant();
    } else if (node->values.count() == 1) {
        // Value
        QVariant &var = node->values[0];
        ModelNode *valueNode = qvariant_cast<ModelNode *>(var);
        if (valueNode) {
            if (!valueNode->properties.isEmpty())
                rv = valueNode->object();
            else
                rv = valueNode->model();
        } else {
            return var;
        }
    } else if (node->values.count() > 1) {
        // List
        rv = node->model();
    }

    if (rv)
        return QVariant::fromValue(rv);
    else
        return QVariant();
}

QHash<int,QVariant> QmlListModel::data(int index, const QList<int> &roles) const
{
    checkRoles();
    QHash<int, QVariant> rv;
    if (index >= count())
        return rv;

    ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
    if (!node)
        return rv;

    for (int ii = 0; ii < roles.count(); ++ii) {
        const QString &roleString = roleStrings.at(roles.at(ii));

        QHash<QString, ModelNode *>::ConstIterator iter =
            node->properties.find(roleString);
        if (iter != node->properties.end()) {
            ModelNode *row = *iter;
            rv.insert(roles.at(ii), valueForNode(row));
        }
    }

    return rv;
}

/*!
    \qmlproperty int ListModel::count
    The number of data entries in the model.
*/
int QmlListModel::count() const
{
    if (!_root) return 0;
    return _root->values.count();
}

class QmlListModelParser : public QmlCustomParser
{
public:
    QByteArray compile(const QList<QmlCustomParserProperty> &, bool *ok);
    bool compileProperty(const QmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data);
    void setCustomData(QObject *, const QByteArray &);
};

bool QmlListModelParser::compileProperty(const QmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data)
{
    QList<QVariant> values = prop.assignedValues();
    for(int ii = 0; ii < values.count(); ++ii) {
        const QVariant &value = values.at(ii);

        if(value.userType() == qMetaTypeId<QmlCustomParserNode>()) {
            QmlCustomParserNode node =
                qvariant_cast<QmlCustomParserNode>(value);

            {
            ListInstruction li;
            li.type = ListInstruction::Push;
            li.dataIdx = -1;
            instr << li;
            }

            QList<QmlCustomParserProperty> props = node.properties();
            for(int jj = 0; jj < props.count(); ++jj) {
                const QmlCustomParserProperty &nodeProp = props.at(jj);
                if(nodeProp.name() == "")
                    return false;

                ListInstruction li;
                int ref = data.count();
                data.append(nodeProp.name());
                data.append('\0');
                li.type = ListInstruction::Set;
                li.dataIdx = ref;
                instr << li;

                if(!compileProperty(nodeProp, instr, data))
                    return false;

                li.type = ListInstruction::Pop;
                li.dataIdx = -1;
                instr << li;
            }

            {
            ListInstruction li;
            li.type = ListInstruction::Pop;
            li.dataIdx = -1;
            instr << li;
            }

        } else {

            QmlParser::Variant variant =
                qvariant_cast<QmlParser::Variant>(value);

            int ref = data.count();
            QByteArray d = variant.asScript().toLatin1();
            d.append('\0');
            data.append(d);

            ListInstruction li;
            li.type = ListInstruction::Value;
            li.dataIdx = ref;
            instr << li;

        }
    }

    return true;
}

QByteArray QmlListModelParser::compile(const QList<QmlCustomParserProperty> &customProps, bool *ok)
{
    *ok = true;
    QList<ListInstruction> instr;
    QByteArray data;

    for(int ii = 0; ii < customProps.count(); ++ii) {
        const QmlCustomParserProperty &prop = customProps.at(ii);
        if(prop.name() != "") { // isn't default property
            *ok = false;
            return QByteArray();
        }

        if(!compileProperty(prop, instr, data)) {
            *ok = false;
            return QByteArray();
        }
    }

    int size = sizeof(ListModelData) +
               instr.count() * sizeof(ListInstruction) +
               data.count();

    QByteArray rv;
    rv.resize(size);

    ListModelData *lmd = (ListModelData *)rv.data();
    lmd->dataOffset = sizeof(ListModelData) +
                     instr.count() * sizeof(ListInstruction);
    lmd->instrCount = instr.count();
    for (int ii = 0; ii < instr.count(); ++ii)
        lmd->instructions()[ii] = instr.at(ii);
    ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count());

    return rv;
}

void QmlListModelParser::setCustomData(QObject *obj, const QByteArray &d)
{
    QmlListModel *rv = static_cast<QmlListModel *>(obj);

    ModelNode *root = new ModelNode;
    rv->_root = root;
    QStack<ModelNode *> nodes;
    nodes << root;

    const ListModelData *lmd = (const ListModelData *)d.constData();
    const char *data = ((const char *)lmd) + lmd->dataOffset;

    for (int ii = 0; ii < lmd->instrCount; ++ii) {
        const ListInstruction &instr = lmd->instructions()[ii];

        switch(instr.type) {
        case ListInstruction::Push:
            {
                ModelNode *n = nodes.top();
                ModelNode *n2 = new ModelNode;
                n->values << qVariantFromValue(n2);
                nodes.push(n2);
            }
            break;

        case ListInstruction::Pop:
            nodes.pop();
            break;

        case ListInstruction::Value:
            {
                ModelNode *n = nodes.top();
                n->values.append(QByteArray(data + instr.dataIdx));
            }
            break;

        case ListInstruction::Set:
            {
                ModelNode *n = nodes.top();
                ModelNode *n2 = new ModelNode;
                n->properties.insert(QLatin1String(data + instr.dataIdx), n2);
                nodes.push(n2);
            }
            break;
        }
    }
}

QML_DEFINE_CUSTOM_TYPE(Qt, 4,6, (QT_VERSION&0x00ff00)>>8, ListModel, QmlListModel, QmlListModelParser)

// ### FIXME
class QmlListElement : public QObject
{
Q_OBJECT
};
QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ListElement,QmlListElement)

static void dump(ModelNode *node, int ind)
{
    QByteArray indentBa(ind * 4, ' ');
    const char *indent = indentBa.constData();

    for (int ii = 0; ii < node->values.count(); ++ii) {
        ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii));
        if (subNode) {
            qWarning().nospace() << indent << "Sub-node " << ii << ": class " << subNode->className;
            dump(subNode, ind + 1);
        } else {
            qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString();
        }
    }

    for (QHash<QString, ModelNode *>::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) {
        qWarning().nospace() << indent << "Property " << iter.key() << ":";
        dump(iter.value(), ind + 1);
    }
}

ModelNode::ModelNode()
: modelCache(0), objectCache(0)
{
}

ModelNode::~ModelNode()
{
    qDeleteAll(properties);
    for (int ii = 0; ii < values.count(); ++ii) {
        ModelNode *node = qvariant_cast<ModelNode *>(values.at(ii));
        if (node) { delete node; node = 0; }
    }
    if (modelCache) { delete modelCache; modelCache = 0; }
}

QT_END_NAMESPACE

Q_DECLARE_METATYPE(ModelNode *)
QML_DECLARE_TYPE(QmlListElement)

#include "qmllistmodel.moc"
<|endoftext|>