1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-01 01:51:58 +02:00

Merge branch 'master' into jreen-tpqt4-comp

Conflicts:
	CMakeLists.txt
	src/tomahawkapp.cpp
This commit is contained in:
Dominik Schmidt
2011-04-06 06:58:33 +02:00
121 changed files with 5936 additions and 1997 deletions

View File

@@ -7,7 +7,7 @@ CMAKE_MINIMUM_REQUIRED( VERSION 2.8 )
SET( TOMAHAWK_ORGANIZATION_NAME "Tomahawk" )
SET( TOMAHAWK_ORGANIZATION_DOMAIN "tomahawk-player.org" )
SET( TOMAHAWK_APPLICATION_NAME "Tomahawk" )
SET( TOMAHAWK_VERSION "0.0.2" )
SET( TOMAHAWK_VERSION "0.0.3" )
# set paths

View File

@@ -1,7 +1,25 @@
Version 0.1.0:
* Watch folders for changes and automatically update your collection. This
is on by default; you can turn it off on the Local Music tab in the
settings dialog. Note that this triggers only on files or folders being
added to or removed from folders; it is not watch individual files as
most OSes can't support enough file watches to handle a normal-sized
music collection.
Version 0.0.3:
* Fix crashes in Twitter authentication. For reals now.
* Fixed an issue which caused duplicate items when rescanning.
* Revert change introduced in 0.0.2 causing Twitter protocol to not try
to reconnect to a peer if it couldn't connect the first time the plugin
was connected. This caused confusing (and for most unwanted) behavior.
* Fix crashes in Twitter authentication.
* Properly honor the chosen port number if a static host and port are
marked as preferred.
* Don't automatically try to resolve all incoming playback logs. This
speeds up importing sources a lot.
* Faster painting of playlists with lots of unresolved tracks.
* The tomahawk:// protocol handler works on Windows now.
* Fixed launching Tomahawk from Windows installer with admin privileges.
* Prefer local results when results' score is equal.
Version 0.0.2:
* Don't reconnect to Jabber if the settings dialog is closed successfully

View File

0
admin/win/nsi/RELEASE_NOTES.txt Executable file → Normal file
View File

0
admin/win/nsi/installer.ico Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

View File

@@ -0,0 +1,35 @@
{\rtf1\ansi\ansicpg1252\uc1\deff0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f39\fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Verdana;}
{\f172\froman\fcharset238\fprq2 Times New Roman CE;}{\f173\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f175\froman\fcharset161\fprq2 Times New Roman Greek;}{\f176\froman\fcharset162\fprq2 Times New Roman Tur;}
{\f177\froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f178\froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f179\froman\fcharset186\fprq2 Times New Roman Baltic;}{\f180\froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
{\f562\fswiss\fcharset238\fprq2 Verdana CE;}{\f563\fswiss\fcharset204\fprq2 Verdana Cyr;}{\f565\fswiss\fcharset161\fprq2 Verdana Greek;}{\f566\fswiss\fcharset162\fprq2 Verdana Tur;}{\f569\fswiss\fcharset186\fprq2 Verdana Baltic;}
{\f570\fswiss\fcharset163\fprq2 Verdana (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;
\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\stylesheet{
\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 Normal;}{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\*
\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv
\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \snext11 \ssemihidden Normal Table;}{\*\cs15 \additive \ul\cf2 \sbasedon10 \styrsid7485074 Hyperlink;}}
{\*\latentstyles\lsdstimax156\lsdlockeddef0}{\*\rsidtbl \rsid6712196\rsid7485074\rsid11352300\rsid15940516}{\*\generator Microsoft Word 11.0.5604;}{\info{\title Processes v1}{\author Hardwired}{\operator Hardwired}{\creatim\yr2004\mo12\dy12\hr23\min42}
{\revtim\yr2004\mo12\dy12\hr23\min51}{\version2}{\edmins9}{\nofpages1}{\nofwords80}{\nofchars458}{\nofcharsws537}{\vern24689}}\widowctrl\ftnbj\aenddoc\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180
\dgvspace180\dghorigin1800\dgvorigin1440\dghshow1\dgvshow1
\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct
\asianbrkrule\rsidroot7485074\newtblstyruls\nogrowautofit \fet0\sectd \linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}
{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}
{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain
\qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7485074 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\b\f39\insrsid7485074\charrsid7485074 Processes v1.0}{\f39\insrsid7485074\charrsid7485074 .0.1
\par }{\f39\fs20\insrsid7485074
\par }\pard \qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid15940516 {\f39\fs20\insrsid15940516 This software binaries and source-code are free for any kind of use, including commercial use. }{
\f39\fs20\insrsid7485074\charrsid7485074 There is no restriction and no guaranty for using}{\f39\fs20\insrsid7485074\charrsid7485074 t}{\f39\fs20\insrsid7485074\charrsid7485074 his software}{\f39\fs20\insrsid7485074\charrsid7485074 and/or it
s source-code. }{\f39\fs20\insrsid15940516
\par I}{\f39\fs20\insrsid7485074\charrsid7485074 f you use the plug}{\f39\fs20\insrsid7485074\charrsid7485074 -}{\f39\fs20\insrsid7485074\charrsid7485074 in }{\f39\fs20\insrsid7485074\charrsid7485074 and/}{\f39\fs20\insrsid7485074\charrsid7485074 or it}{
\f39\fs20\insrsid7485074\charrsid7485074 s}{\f39\fs20\insrsid7485074\charrsid7485074 source-code, I would }{\f39\fs20\insrsid7485074\charrsid7485074 appreciate }{\f39\fs20\insrsid7485074\charrsid7485074 if my name is mentioned.}{
\f39\fs20\insrsid7485074\charrsid7485074
\par }\pard \qj \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7485074 {\f39\fs20\insrsid7485074\charrsid7485074
\par }{\b\f39\fs20\insrsid7485074\charrsid7485074 Andrei Ciubotaru [Hardwired]
\par }{\f39\fs20\insrsid7485074\charrsid7485074 Lead Developer ICode&Ideas SRL (}{\field\flddirty{\*\fldinst {\f39\fs20\insrsid7485074\charrsid7485074 HYPERLINK "http://www.icode.ro/" }{\f39\fs20\insrsid7485074\charrsid7485074 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b02000000170000001500000068007400740070003a002f002f007700770077002e00690063006f00640065002e0072006f002f000000e0c9ea79f9bace118c8200aa004ba90b2a00000068007400740070003a002f002f007700770077002e00690063006f00640065002e007200
6f002f000000}}}{\fldrslt {\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 http://www.icode.ro/}}}{\f39\fs20\insrsid7485074\charrsid7485074 )
\par }{\field{\*\fldinst {\f39\fs20\insrsid7485074 HYPERLINK "hardwiredteks@gmail.com" }{\f39\fs20\insrsid15940516\charrsid7485074 {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c00000000000004600001800000068617264776972656474656b7340676d61696c2e636f6d00ffffadde000000000000000000000000000000000000000000000000}}}{\fldrslt {
\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 hardwiredteks@gmail.com}}}{\f39\fs20\insrsid7485074\charrsid7485074 , }{\field{\*\fldinst {\f39\fs20\insrsid7485074 HYPERLINK "hardwired@icode.ro" }{\f39\fs20\insrsid15940516\charrsid7485074
{\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b02000000010000000303000000000000c0000000000000460000130000006861726477697265644069636f64652e726f00ffffadde000000000000000000000000000000000000000000000000}}}{\fldrslt {
\cs15\f39\fs20\ul\cf2\insrsid7485074\charrsid7485074 hardwired@icode.ro}}}{\f39\fs20\insrsid7485074\charrsid7485074
\par }}

View File

@@ -0,0 +1,122 @@
----------------------------------------------------------------
----------------------------------------------------------------
Processes (Processes.dll)
Version: 1.0.1.0
Release: 24.february.2005
Description: Nullsoft Installer (NSIS) plug-in for managing?!
Windows processes.
Copyright: <09> 2004-2005 Hardwired. No rights reserved.
There is no restriction and no guaranty for using
this software.
Author: Andrei Ciubotaru [Hardwired]
Lead Developer ICode&Ideas SRL (http://www.icode.ro/)
hardwiredteks@gmail.com, hardwired@icode.ro
----------------------------------------------------------------
----------------------------------------------------------------
INTRODUCTION
The Need For Plug-in - I need it for the one of my installers.
Briefly: Use it when you need to find\kill a process when
installing\uninstalling some application. Also, use it when you
need to test the presence of a device driver.
SUPPORT
Supported platforms are: WinNT,Win2K,WinXP and Win2003 Server.
DESCRIPTION
Processes::FindProcess <process_name> ;without ".exe"
Searches the currently running processes for the given
process name.
return: 1 - the process was found
0 - the process was not found
Processes::KillProcess <process_name> ; without ".exe"
Searches the currently running processes for the given
process name. If the process is found then the it gets
killed.
return: 1 - the process was found and killed
0 - the process was not found or the process
cannot be killed (insuficient rights)
Processes::FindDevice <device_base_name>
Searches the installed devices drivers for the given
device base name.
(important: I said BASE NAME not FILENAME)
return: 1 - the device driver was found
0 - the device driver was not found
USAGE
First of all, does not matter where you use it. Ofcourse, the
routines must be called inside of a Section/Function scope.
Processes::FindProcess "process_name"
Pop $R0
StrCmp $R0 "1" make_my_day noooooo
make_my_day:
...
noooooo:
...
Processes::KillProcess "process_name"
Pop $R0
StrCmp $R0 "1" dead_meat why_wont_you_die
dead_meat:
...
why_wont_you_die:
...
Processes::FindDevice "device_base_name"
Pop $R0
StrCmp $R0 "1" blabla more_blabla
blabla:
...
more_blabla:
...
THANKS
Sunil Kamath for inspiring me. I wanted to use its FindProcDLL
but my requirements made it imposible.
Nullsoft for creating this very powerfull installer. One big,
free and full-featured (hmmm... and guiless for the moment) mean
install machine!:)
ME for being such a great coder...
... HAHAHAHAHAHAHA!
ONE MORE THING
If you use the plugin or it's source-code, I would apreciate
if my name is mentioned.
----------------------------------------------------------------
----------------------------------------------------------------

View File

@@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// KillProcDLL.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

View File

@@ -0,0 +1,34 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#if !defined(AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_)
#define AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <string> // String management...
//From exam28.cpp
#include <tlhelp32.h>
//#include <iostream.h>
#ifdef BORLANDC
#include <string.h>
#include <ctype.h>
#endif
//To make it a NSIS Plug-In
#include "exdll.h"
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_STDAFX_H__780690DC_E128_403D_BC07_780D1B2CC101__INCLUDED_)

View File

@@ -0,0 +1,37 @@
#include <windows.h>
#include "exdll.h"
HINSTANCE g_hInstance;
HWND g_hwndParent;
void __declspec(dllexport) myFunction(HWND hwndParent, int string_size,
char *variables, stack_t **stacktop)
{
g_hwndParent=hwndParent;
EXDLL_INIT();
// note if you want parameters from the stack, pop them off in order.
// i.e. if you are called via exdll::myFunction file.dat poop.dat
// calling popstring() the first time would give you file.dat,
// and the second time would give you poop.dat.
// you should empty the stack of your parameters, and ONLY your
// parameters.
// do your stuff here
{
char buf[1024];
wsprintf(buf,"$0=%s\n",getuservariable(INST_0));
MessageBox(g_hwndParent,buf,0,MB_OK);
}
}
BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
g_hInstance=hInst;
return TRUE;
}

View File

@@ -0,0 +1,136 @@
#ifndef _EXDLL_H_
#define _EXDLL_H_
//
// only include this file from one place in your DLL.
// (it is all static, if you use it in two places it will fail)
//
#define EXDLL_INIT() { \
g_stringsize = string_size; \
g_stacktop = stacktop; \
g_variables = variables; }
//
// For page showing plug-ins
//
#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8)
#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd)
#define NOTIFY_BYE_BYE 'x'
typedef struct _stack_t
{
struct _stack_t *next;
char text[1]; // this should be the length of string_size
} stack_t;
static unsigned int g_stringsize;
static stack_t **g_stacktop;
static char *g_variables;
enum
{
INST_0, // $0
INST_1, // $1
INST_2, // $2
INST_3, // $3
INST_4, // $4
INST_5, // $5
INST_6, // $6
INST_7, // $7
INST_8, // $8
INST_9, // $9
INST_R0, // $R0
INST_R1, // $R1
INST_R2, // $R2
INST_R3, // $R3
INST_R4, // $R4
INST_R5, // $R5
INST_R6, // $R6
INST_R7, // $R7
INST_R8, // $R8
INST_R9, // $R9
INST_CMDLINE, // $CMDLINE
INST_INSTDIR, // $INSTDIR
INST_OUTDIR, // $OUTDIR
INST_EXEDIR, // $EXEDIR
INST_LANG, // $LANGUAGE
__INST_LAST
};
//
// utility functions (not required but often useful)
//
static int popstring( char *str )
{
stack_t *th;
if( !g_stacktop ||
!*g_stacktop )
return 1;
th = (*g_stacktop);
lstrcpy( str, th->text );
*g_stacktop = th->next;
GlobalFree( (HGLOBAL)th );
return 0;
}
static void pushstring( char *str )
{
stack_t *th;
if( !g_stacktop )
return;
th = (stack_t*)GlobalAlloc( GPTR, sizeof(stack_t) + g_stringsize );
lstrcpyn( th->text, str, g_stringsize );
th->next = *g_stacktop;
*g_stacktop = th;
}
static char *getuservariable( int varnum )
{
if( varnum < 0 ||
varnum >= __INST_LAST )
return NULL;
return (g_variables + varnum*g_stringsize);
}
static void setuservariable( int varnum, char *var )
{
if( var != NULL &&
varnum >= 0 &&
varnum < __INST_LAST )
lstrcpy( g_variables + varnum*g_stringsize, var );
}
#endif//_EXDLL_H_

View File

@@ -0,0 +1,411 @@
#include "stdafx.h"
#include "processes.h"
#include "string.h"
//-------------------------------------------------------------------------------------------
// global variables
lpfEnumProcesses EnumProcesses;
lpfEnumProcessModules EnumProcessModules;
lpfGetModuleBaseName GetModuleBaseName;
lpfEnumDeviceDrivers EnumDeviceDrivers;
lpfGetDeviceDriverBaseName GetDeviceDriverBaseName;
HINSTANCE g_hInstance;
HWND g_hwndParent;
HINSTANCE g_hInstLib;
//-------------------------------------------------------------------------------------------
// main DLL entry
BOOL WINAPI _DllMainCRTStartup( HANDLE hInst,
ULONG ul_reason_for_call,
LPVOID lpReserved )
{
g_hInstance = (struct HINSTANCE__ *)hInst;
return TRUE;
}
//-------------------------------------------------------------------------------------------
// loads the psapi routines
bool LoadPSAPIRoutines( void )
{
if( NULL == (g_hInstLib = LoadLibraryA( "PSAPI.DLL" )) )
return false;
EnumProcesses = (lpfEnumProcesses) GetProcAddress( g_hInstLib, "EnumProcesses" );
EnumProcessModules = (lpfEnumProcessModules) GetProcAddress( g_hInstLib, "EnumProcessModules" );
GetModuleBaseName = (lpfGetModuleBaseName) GetProcAddress( g_hInstLib, "GetModuleBaseNameA" );
EnumDeviceDrivers = (lpfEnumDeviceDrivers) GetProcAddress( g_hInstLib, "EnumDeviceDrivers" );
GetDeviceDriverBaseName = (lpfGetDeviceDriverBaseName) GetProcAddress( g_hInstLib, "GetDeviceDriverBaseNameA" );
if( ( NULL == EnumProcesses ) ||
( NULL == EnumProcessModules ) ||
( NULL == EnumDeviceDrivers ) ||
( NULL == GetModuleBaseName ) ||
( NULL == GetDeviceDriverBaseName ) )
{
FreeLibrary( g_hInstLib );
return false;
}
return true;
}
//-------------------------------------------------------------------------------------------
// free the psapi routines
bool FreePSAPIRoutines( void )
{
EnumProcesses = NULL;
EnumProcessModules = NULL;
GetModuleBaseName = NULL;
EnumDeviceDrivers = NULL;
if( FALSE == FreeLibrary( g_hInstLib ) )
return false;
return true;
}
//-------------------------------------------------------------------------------------------
// find a process by name
// return value: true - process was found
// false - process not found
bool FindProc( char *szProcess )
{
char szProcessName[ 1024 ];
char szCurrentProcessName[ 1024 ];
DWORD dPID[ 1024 ];
DWORD dPIDSize( 1024 );
DWORD dSize( 1024 );
HANDLE hProcess;
HMODULE phModule[ 1024 ];
//
// make the name lower case
//
memset( szProcessName, 0, 1024*sizeof(char) );
sprintf( szProcessName, "%s", szProcess );
strlwr( szProcessName );
//
// load PSAPI routines
//
if( false == LoadPSAPIRoutines() )
return false;
//
// enumerate processes names
//
if( FALSE == EnumProcesses( dPID, dSize, &dPIDSize ) )
{
FreePSAPIRoutines();
return false;
}
//
// walk trough and compare see if the process is running
//
for( int k( dPIDSize / sizeof( DWORD ) ); k >= 0; k-- )
{
memset( szCurrentProcessName, 0, 1024*sizeof(char) );
if( NULL != ( hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dPID[ k ] ) ) )
{
if( TRUE == EnumProcessModules( hProcess, phModule, sizeof(HMODULE)*1024, &dPIDSize ) )
if( GetModuleBaseName( hProcess, phModule[ 0 ], szCurrentProcessName, 1024 ) > 0 )
{
strlwr( szCurrentProcessName );
if( NULL != strstr( szCurrentProcessName, szProcessName ) )
{
FreePSAPIRoutines();
CloseHandle( hProcess );
return true;
}
}
CloseHandle( hProcess );
}
}
//
// free PSAPI routines
//
FreePSAPIRoutines();
return false;
}
//-------------------------------------------------------------------------------------------
// kills a process by name
// return value: true - process was found
// false - process not found
bool KillProc( char *szProcess )
{
char szProcessName[ 1024 ];
char szCurrentProcessName[ 1024 ];
DWORD dPID[ 1024 ];
DWORD dPIDSize( 1024 );
DWORD dSize( 1024 );
HANDLE hProcess;
HMODULE phModule[ 1024 ];
//
// make the name lower case
//
memset( szProcessName, 0, 1024*sizeof(char) );
sprintf( szProcessName, "%s", szProcess );
strlwr( szProcessName );
//
// load PSAPI routines
//
if( false == LoadPSAPIRoutines() )
return false;
//
// enumerate processes names
//
if( FALSE == EnumProcesses( dPID, dSize, &dPIDSize ) )
{
FreePSAPIRoutines();
return false;
}
//
// walk trough and compare see if the process is running
//
for( int k( dPIDSize / sizeof( DWORD ) ); k >= 0; k-- )
{
memset( szCurrentProcessName, 0, 1024*sizeof(char) );
if( NULL != ( hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dPID[ k ] ) ) )
{
if( TRUE == EnumProcessModules( hProcess, phModule, sizeof(HMODULE)*1024, &dPIDSize ) )
if( GetModuleBaseName( hProcess, phModule[ 0 ], szCurrentProcessName, 1024 ) > 0 )
{
strlwr( szCurrentProcessName );
if( NULL != strstr( szCurrentProcessName, szProcessName ) )
{
FreePSAPIRoutines();
//
// kill process
//
if( false == TerminateProcess( hProcess, 0 ) )
{
CloseHandle( hProcess );
return true;
}
//
// refresh systray
//
UpdateWindow( FindWindow( NULL, "Shell_TrayWnd" ) );
//
// refresh desktop window
//
UpdateWindow( GetDesktopWindow() );
CloseHandle( hProcess );
return true;
}
}
CloseHandle( hProcess );
}
}
//
// free PSAPI routines
//
FreePSAPIRoutines();
return false;
}
//-------------------------------------------------------------------------------------------
bool FindDev( char *szDriverName )
{
char szDeviceName[ 1024 ];
char szCurrentDeviceName[ 1024 ];
LPVOID lpDevices[ 1024 ];
DWORD dDevicesSize( 1024 );
DWORD dSize( 1024 );
TCHAR tszCurrentDeviceName[ 1024 ];
DWORD dNameSize( 1024 );
//
// make the name lower case
//
memset( szDeviceName, 0, 1024*sizeof(char) );
sprintf( szDeviceName, "%s", strlwr( szDriverName ) );
//
// load PSAPI routines
//
if( false == LoadPSAPIRoutines() )
return false;
//
// enumerate devices
//
if( FALSE == EnumDeviceDrivers( lpDevices, dSize, &dDevicesSize ) )
{
FreePSAPIRoutines();
return false;
}
//
// walk trough and compare see if the device driver exists
//
for( int k( dDevicesSize / sizeof( LPVOID ) ); k >= 0; k-- )
{
memset( szCurrentDeviceName, 0, 1024*sizeof(char) );
memset( tszCurrentDeviceName, 0, 1024*sizeof(TCHAR) );
if( 0 != GetDeviceDriverBaseName( lpDevices[ k ], tszCurrentDeviceName, dNameSize ) )
{
sprintf( szCurrentDeviceName, "%S", tszCurrentDeviceName );
if( 0 == strcmp( strlwr( szCurrentDeviceName ), szDeviceName ) )
{
FreePSAPIRoutines();
return true;
}
}
}
//
// free PSAPI routines
//
FreePSAPIRoutines();
return false;
}
//-------------------------------------------------------------------------------------------
extern "C" __declspec(dllexport) void FindProcess( HWND hwndParent,
int string_size,
char *variables,
stack_t **stacktop )
{
char szParameter[ 1024 ];
g_hwndParent = hwndParent;
EXDLL_INIT();
{
popstring( szParameter );
if( true == FindProc( szParameter ) )
wsprintf( szParameter, "1" );
else
wsprintf( szParameter, "0" );
setuservariable( INST_R0, szParameter );
}
}
//-------------------------------------------------------------------------------------------
extern "C" __declspec(dllexport) void KillProcess( HWND hwndParent,
int string_size,
char *variables,
stack_t **stacktop )
{
char szParameter[ 1024 ];
g_hwndParent = hwndParent;
EXDLL_INIT();
{
popstring( szParameter );
if( true == KillProc( szParameter ) )
wsprintf( szParameter, "1" );
else
wsprintf( szParameter, "0" );
setuservariable( INST_R0, szParameter );
}
}
//-------------------------------------------------------------------------------------------
extern "C" __declspec(dllexport) void FindDevice( HWND hwndParent,
int string_size,
char *variables,
stack_t **stacktop )
{
char szParameter[ 1024 ];
g_hwndParent = hwndParent;
EXDLL_INIT();
{
popstring( szParameter );
if( true == FindDev( szParameter ) )
wsprintf( szParameter, "1" );
else
wsprintf( szParameter, "0" );
setuservariable( INST_R0, szParameter );
}
}

View File

@@ -0,0 +1,49 @@
#pragma once
//-------------------------------------------------------------------------------------------
// PSAPI function pointers
typedef BOOL (WINAPI *lpfEnumProcesses) ( DWORD *, DWORD, DWORD * );
typedef BOOL (WINAPI *lpfEnumProcessModules) ( HANDLE, HMODULE *, DWORD, LPDWORD );
typedef DWORD (WINAPI *lpfGetModuleBaseName) ( HANDLE, HMODULE, LPTSTR, DWORD );
typedef BOOL (WINAPI *lpfEnumDeviceDrivers) ( LPVOID *, DWORD, LPDWORD );
typedef BOOL (WINAPI *lpfGetDeviceDriverBaseName)( LPVOID, LPTSTR, DWORD );
//-------------------------------------------------------------------------------------------
// Internal use routines
bool LoadPSAPIRoutines( void );
bool FreePSAPIRoutines( void );
bool FindProc( char *szProcess );
bool KillProc( char *szProcess );
bool FindDev( char *szDriverName );
//-------------------------------------------------------------------------------------------
// Exported routines
extern "C" __declspec(dllexport) void FindProcess( HWND hwndParent,
int string_size,
char *variables,
stack_t **stacktop );
extern "C" __declspec(dllexport) void KillProcess( HWND hwndParent,
int string_size,
char *variables,
stack_t **stacktop );
extern "C" __declspec(dllexport) void FindDevice( HWND hwndParent,
int string_size,
char *variables,
stack_t **stacktop );

Binary file not shown.

View File

@@ -0,0 +1,103 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "NSIS Plug-in for Windows process management. Only WinNT, Win2K, WinXP and Win2003 Server supported."
VALUE "CompanyName", "Andrei Ciubotaru [Hardwired]"
VALUE "FileDescription", "Windows Processes Management"
VALUE "FileVersion", "1, 0, 0, 1"
VALUE "InternalName", "Processes"
VALUE "LegalCopyright", "Copyright (c) 2004 Hardwired. No rights reserved."
VALUE "OriginalFilename", "Processes.dll"
VALUE "ProductName", "Processes"
VALUE "ProductVersion", "1, 0, 0, 1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@@ -0,0 +1,21 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "processes", "processes.vcproj", "{3438467F-A719-46DC-93E5-137A8B691727}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
Release = Release
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{3438467F-A719-46DC-93E5-137A8B691727}.Debug.ActiveCfg = Debug|Win32
{3438467F-A719-46DC-93E5-137A8B691727}.Debug.Build.0 = Debug|Win32
{3438467F-A719-46DC-93E5-137A8B691727}.Release.ActiveCfg = Release|Win32
{3438467F-A719-46DC-93E5-137A8B691727}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,122 @@
----------------------------------------------------------------
----------------------------------------------------------------
Processes (Processes.dll)
Version: 1.0.0.1
Release: 12.december.2004
Description:Nullsoft Installer (NSIS) plug-in for managing?!
Windows processes.
Copyright: <09> 2004 Hardwired. No rights reserved.
There is no restriction and no guaranty for using
this software.
Author: Andrei Ciubotaru [Hardwired]
Lead Developer ICode&Ideas SRL (http://www.icode.ro)
hardwiredteks@gmail.com, hardwired@icode.ro
----------------------------------------------------------------
----------------------------------------------------------------
INTRODUCTION
The Need For Plug-in - I need it for the one of my installers.
Briefly: Use it when you need to find\kill a process when
installing\uninstalling some application. Also, use it when you
need to test the presence of a device driver.
SUPPORT
Supported platforms are: WinNT,Win2K,WinXP and Win2003 Server.
DESCRIPTION
Processes::FindProcess <process_name.exe>
Searches the currently running processes for the given
process name.
return: 1 - the process was found
0 - the process was not found
Processes::KillProcess <process_name.exe>
Searches the currently running processes for the given
process name. If the process is found then the it gets
killed.
return: 1 - the process was found and killed
0 - the process was not found or the process
cannot be killed (insuficient rights)
Processes::FindDevice <device_base_name>
Searches the installed devices drivers for the given
device base name.
(important: I said BASE NAME not FILENAME)
return: 1 - the device driver was found
0 - the device driver was not found
USAGE
First of all, does not matter where you use it. Ofcourse, the
routines must be called inside of a Section/Function scope.
Processes::FindProcess "process_name.exe"
Pop $R0
StrCmp $R0 "1" make_my_day noooooo
make_my_day:
...
noooooo:
...
Processes::KillProcess "process_name.exe"
Pop $R0
StrCmp $R0 "1" dead_meat why_wont_you_die
dead_meat:
...
why_wont_you_die:
...
Processes::FindDevice "device_base_name"
Pop $R0
StrCmp $R0 "1" blabla more_blabla
blabla:
...
more_blabla:
...
THANKS
Sunil Kamath for inspiring me. I wanted to use its FindProcDLL
but my requirements made it imposible.
Nullsoft for creating this very powerfull installer. One big,
free and full-featured (hmmm... and guiless for the moment) mean
install machine!:)
ME for being such a great coder...
... HAHAHAHAHAHAHA!
ONE MORE THING
If you use the plugin or it's source-code, I would apreciate
if my name is mentioned.
----------------------------------------------------------------
----------------------------------------------------------------

View File

@@ -0,0 +1,222 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="processes"
SccProjectName="processes"
SccLocalPath=".">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="2"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;FINDPROCDLL_EXPORTS"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="3"
PrecompiledHeaderThrough="stdafx.h"
PrecompiledHeaderFile=".\Debug/processes.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="4"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
OutputFile=".\Debug/processes.dll"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile=".\Debug/processes.pdb"
ImportLibrary=".\Debug/processes.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
PreprocessorDefinitions="_DEBUG"
MkTypLibCompatible="TRUE"
SuppressStartupBanner="TRUE"
TargetEnvironment="1"
TypeLibraryName=".\Debug/processes.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1034"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="..\bin"
IntermediateDirectory="..\bin\processes"
ConfigurationType="2"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2"
WholeProgramOptimization="TRUE">
<Tool
Name="VCCLCompilerTool"
Optimization="3"
GlobalOptimizations="TRUE"
InlineFunctionExpansion="1"
FavorSizeOrSpeed="1"
OptimizeForWindowsApplication="TRUE"
PreprocessorDefinitions="NDEBUG;_WINDOWS;_USRDLL;FINDPROCDLL_EXPORTS"
StringPooling="TRUE"
RuntimeLibrary="0"
StructMemberAlignment="1"
EnableFunctionLevelLinking="TRUE"
UsePrecompiledHeader="3"
PrecompiledHeaderThrough="stdafx.h"
PrecompiledHeaderFile="..\bin\processes/processes.pch"
AssemblerListingLocation="..\bin\processes/"
ObjectFile="..\bin\processes/"
ProgramDataBaseFileName="..\bin\processes/"
WarningLevel="4"
SuppressStartupBanner="TRUE"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="libc.lib"
OutputFile="..\bin/Processes.dll"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
IgnoreAllDefaultLibraries="FALSE"
ProgramDatabaseFile="..\bin/processes.pdb"
OptimizeForWindows98="1"
ImportLibrary="..\bin/processes.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
PreprocessorDefinitions="NDEBUG"
MkTypLibCompatible="TRUE"
SuppressStartupBanner="TRUE"
TargetEnvironment="1"
TypeLibraryName=".\Release/processes.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1033"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
<File
RelativePath="processes.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;FINDPROCDLL_EXPORTS;$(NoInherit)"
BasicRuntimeChecks="3"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="1"
PreprocessorDefinitions="NDEBUG;_WINDOWS;_MBCS;_USRDLL;FINDPROCDLL_EXPORTS;$(NoInherit)"/>
</FileConfiguration>
</File>
<File
RelativePath=".\processes.rc">
</File>
<File
RelativePath="StdAfx.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;FINDPROCDLL_EXPORTS;$(NoInherit)"
BasicRuntimeChecks="3"
UsePrecompiledHeader="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="1"
PreprocessorDefinitions="NDEBUG;_WINDOWS;_MBCS;_USRDLL;FINDPROCDLL_EXPORTS;$(NoInherit)"
UsePrecompiledHeader="1"/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl">
<File
RelativePath="exdll.h">
</File>
<File
RelativePath=".\processes.h">
</File>
<File
RelativePath=".\resource.h">
</File>
<File
RelativePath="StdAfx.h">
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
</Filter>
<File
RelativePath="processes.txt">
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,15 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by processes.rc
//
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

0
admin/win/nsi/page_header.bmp Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -1 +1 @@
96
100

0
admin/win/nsi/tomahawk.ini Executable file → Normal file
View File

97
admin/win/nsi/tomahawk.nsi Executable file → Normal file
View File

@@ -4,12 +4,12 @@
; Some installer script options (comment-out options not required)
;-----------------------------------------------------------------------------
;!define OPTION_LICENSE_AGREEMENT
;!define OPTION_UAC_PLUGIN_ENHANCED
!define OPTION_UAC_PLUGIN_ENHANCED
!define OPTION_SECTION_SC_START_MENU
!define OPTION_SECTION_SC_DESKTOP
!define OPTION_SECTION_SC_QUICK_LAUNCH
!define OPTION_FINISHPAGE
!define OPTION_FINISHPAGE_LAUNCHER
;!define OPTION_FINISHPAGE_LAUNCHER
!define OPTION_FINISHPAGE_RELEASE_NOTES
;-----------------------------------------------------------------------------
@@ -35,7 +35,7 @@
!define VER_MAJOR "0"
!define VER_MINOR "0"
!define VER_BUILD "2"
!define VER_BUILD "3"
!define VERSION "${VER_MAJOR}.${VER_MINOR}.${VER_BUILD}"
@@ -58,17 +58,10 @@ InstType Full
InstType Minimal
CRCCheck On
SetCompressor /SOLID lzma
RequestExecutionLevel user ;Now using the UAC plugin.
ReserveFile tomahawk.ini
ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll"
;The UAC plugin provides an elevated user.
;Otherwise request admin level here.
!ifdef OPTION_UAC_PLUGIN_ENHANCED
RequestExecutionLevel user
!else
RequestExecutionLevel admin
!endif
;-----------------------------------------------------------------------------
; Include some required header files.
;-----------------------------------------------------------------------------
@@ -79,9 +72,7 @@ ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll"
!include Memento.nsh ;Remember user selections.
!include WinVer.nsh ;Windows version detection.
!include WordFunc.nsh ;Used by VersionCompare macro function.
!ifdef OPTION_UAC_PLUGIN_ENHANCED
!include UAC.nsh ;Used by the UAC elevation to install as user or admin.
!endif
;-----------------------------------------------------------------------------
; Memento selections stored in registry.
@@ -146,18 +137,46 @@ UninstPage custom un.UnPageUserAppData un.UnPageUserAppDataLeave
##############################################################################
Function LaunchTomahawk
!ifdef OPTION_UAC_PLUGIN_ENHANCED
${UAC.CallFunctionAsUser} LaunchTomahawkAsUser
!else
Exec "$INSTDIR\tomahawk.exe"
!endif
FunctionEnd
!ifdef OPTION_UAC_PLUGIN_ENHANCED
Function LaunchTomahawkAsUser
Exec "$INSTDIR\tomahawk.exe"
FunctionEnd
!endif
##############################################################################
# #
# PROCESS HANDLING FUNCTIONS AND MACROS #
# #
##############################################################################
!macro CheckForProcess processName gotoWhenFound gotoWhenNotFound
Processes::FindProcess ${processName}
StrCmp $R0 "0" ${gotoWhenNotFound} ${gotoWhenFound}
!macroend
!macro ConfirmEndProcess processName
MessageBox MB_YESNO|MB_ICONEXCLAMATION \
"Found ${processName} process(s) which need to be stopped.$\nDo you want the installer to stop these for you?" \
IDYES process_${processName}_kill IDNO process_${processName}_ended
process_${processName}_kill:
DetailPrint "Killing ${processName} processes."
Processes::KillProcess ${processName}
Sleep 1500
StrCmp $R0 "1" process_${processName}_ended
DetailPrint "Process to kill not found!"
process_${processName}_ended:
!macroend
!macro CheckAndConfirmEndProcess processName
!insertmacro CheckForProcess ${processName} 0 no_process_${processName}_to_end
!insertmacro ConfirmEndProcess ${processName}
no_process_${processName}_to_end:
!macroend
Function EnsureTomahawkShutdown
!insertmacro CheckAndConfirmEndProcess "tomahawk.exe"
FunctionEnd
##############################################################################
# #
@@ -226,12 +245,10 @@ Function PageLeaveReinstall
Delete $R1
RMDir $INSTDIR
no_remove_uninstaller:
StrCmp $R0 "2" +2 0
BringToFront
!ifdef OPTION_UAC_PLUGIN_ENHANCED
StrCmp $R0 "2" 0 +3
UAC::Unload
Quit
!endif
BringToFront
reinst_done:
FunctionEnd
@@ -346,7 +363,7 @@ SectionGroup "Shortcuts"
CreateShortCut "$SMPROGRAMS\Tomahawk\LICENSE.lnk" "$INSTDIR\LICENSE.txt"
CreateShortCut "$SMPROGRAMS\Tomahawk\Tomahawk.lnk" "$INSTDIR\tomahawk.exe"
CreateShortCut "$SMPROGRAMS\Tomahawk\Release notes.lnk" "$INSTDIR\NOTES.txt"
CreateShortCut "$SMPROGRAMS\Tomahawk\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
CreateShortCut "$SMPROGRAMS\Tomahawk\Uninstall.lnk" "$INSTDIR\uninstall.exe"
SetShellVarContext current
${MementoSectionEnd}
!endif
@@ -390,7 +407,7 @@ Section -post
SetDetailsPrint textonly
DetailPrint "Writing Uninstaller"
SetDetailsPrint listonly
WriteUninstaller $INSTDIR\Uninstall.exe
WriteUninstaller $INSTDIR\uninstall.exe
;Registry keys required for installer version handling and uninstaller.
SetDetailsPrint textonly
@@ -418,6 +435,12 @@ Section -post
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "NoModify" "1"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "NoRepair" "1"
; Register tomahawk:// protocol handler
WriteRegStr HKCR "tomahawk" "" "URL: Tomahawk Protocol"
WriteRegStr HKCR "tomahawk\DefaultIcon" "" $INSTDIR\tomahawk.exe,1
WriteRegStr HKCR "tomahawk\shell" "" "open"
WriteRegStr HKCR "tomahawk\shell\open\command" "" '"$INSTDIR\tomahawk.exe" "%1"'
SetDetailsPrint textonly
DetailPrint "Finsihed."
SectionEnd
@@ -477,6 +500,8 @@ Section Uninstall
DeleteRegValue HKLM "Software\Tomahawk" ""
DeleteRegKey HKLM "Software\Tomahawk"
DeleteRegKey HKCR "tomahawk"
;Start menu shortcuts.
!ifdef OPTION_SECTION_SC_START_MENU
SetShellVarContext all
@@ -526,7 +551,6 @@ Function .onInit
${MementoSectionRestore}
!ifdef OPTION_UAC_PLUGIN_ENHANCED
UAC_Elevate:
UAC::RunElevated
StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user?
@@ -546,7 +570,6 @@ Function .onInit
StrCmp 3 $1 0 UAC_ElevationAborted ;Try again?
MessageBox MB_ICONSTOP "This installer requires admin access, try again"
goto UAC_Elevate
!endif
;Prevent multiple instances.
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "tomahawkInstaller") i .r1 ?e'
@@ -554,19 +577,25 @@ Function .onInit
StrCmp $R0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running."
Abort
;Use available InstallLocation when possible. This is useful in the uninstaller
;via re-install, which would otherwise use a default location - a bug.
ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tomahawk" "InstallLocation"
StrCmp $R0 "" SkipSetInstDir
StrCpy $INSTDIR $R0
SkipSetInstDir:
;Shutdown Tomahawk in case Add/Remove re-installer option used.
Call EnsureTomahawkShutdown
FunctionEnd
Function .onInstSuccess
${MementoSectionSave}
!ifdef OPTION_UAC_PLUGIN_ENHANCED
UAC::Unload ;Must call unload!
!endif
FunctionEnd
Function .onInstFailed
!ifdef OPTION_UAC_PLUGIN_ENHANCED
UAC::Unload ;Must call unload!
!endif
FunctionEnd
##############################################################################
@@ -576,7 +605,6 @@ FunctionEnd
##############################################################################
Function un.onInit
!ifdef OPTION_UAC_PLUGIN_ENHANCED
UAC_Elevate:
UAC::RunElevated
StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user?
@@ -596,7 +624,6 @@ Function un.onInit
StrCmp 3 $1 0 UAC_ElevationAborted ;Try again?
MessageBox MB_ICONSTOP "This uninstaller requires admin access, try again"
goto UAC_Elevate
!endif
;Prevent multiple instances.
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "tomahawkUninstaller") i .r1 ?e'
@@ -607,13 +634,9 @@ Function un.onInit
FunctionEnd
Function un.onUnInstSuccess
!ifdef OPTION_UAC_PLUGIN_ENHANCED
UAC::Unload ;Must call unload!
!endif
FunctionEnd
Function un.onUnInstFailed
!ifdef OPTION_UAC_PLUGIN_ENHANCED
UAC::Unload ;Must call unload!
!endif
FunctionEnd

0
admin/win/nsi/welcome.bmp Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 151 KiB

View File

@@ -19,6 +19,7 @@
#ifndef TOMAHAWK_INFOSYSTEM_H
#define TOMAHAWK_INFOSYSTEM_H
#include <QtCore/QCryptographicHash>
#include <QtCore/QObject>
#include <QtCore/QtDebug>
#include <QtCore/QMap>
@@ -93,7 +94,8 @@ enum InfoType {
typedef QMap< InfoType, QVariant > InfoMap;
typedef QMap< QString, QMap< QString, QString > > InfoGenericMap;
typedef QHash< QString, QVariant > InfoCustomDataHash;
typedef QHash< QString, QVariant > InfoCustomData;
typedef QHash< QString, QString > InfoCacheCriteria;
class InfoPlugin : public QObject
{
@@ -107,15 +109,19 @@ public:
qDebug() << Q_FUNC_INFO;
}
virtual void getInfo( const QString &caller, const InfoType type, const QVariant &data, Tomahawk::InfoSystem::InfoCustomDataHash customData ) = 0;
virtual void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData ) = 0;
signals:
void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData );
void getCachedInfo( QHash< QString, QString > criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData );
void getCachedInfo( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData );
void updateCache( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output );
void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData );
void finished( QString, Tomahawk::InfoSystem::InfoType );
//public slots:
//void notInCacheSlot( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData ) = 0;
public slots:
//FIXME: Make pure virtual when everything supports it
virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData )
{
}
protected:
InfoType m_type;
@@ -134,18 +140,17 @@ public:
void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &types );
void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData );
void getInfo( const QString &caller, const InfoMap &input, InfoCustomDataHash customData );
void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData );
void getInfo( const QString &caller, const InfoMap &input, InfoCustomData customData );
InfoSystemCache* getCache() { return m_cache; }
signals:
void info( QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData );
void info( QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData );
void finished( QString target );
public slots:
void infoSlot( QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData );
void finishedSlot( QString target,Tomahawk::InfoSystem::InfoType type);
void infoSlot( QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData );
private:
QLinkedList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const;
@@ -155,7 +160,7 @@ private:
// For now, statically instantiate plugins; this is just somewhere to keep them
QLinkedList< InfoPluginPtr > m_plugins;
QHash< QString, QHash< Tomahawk::InfoSystem::InfoType, int > > m_dataTracker;
QHash< QString, QHash< InfoType, int > > m_dataTracker;
InfoSystemCache* m_cache;
QThread* m_infoSystemCacheThreadController;
@@ -165,7 +170,26 @@ private:
}
inline uint qHash( Tomahawk::InfoSystem::InfoCacheCriteria hash )
{
QCryptographicHash md5( QCryptographicHash::Md5 );
foreach( QString key, hash.keys() )
md5.addData( key.toUtf8() );
foreach( QString value, hash.values() )
md5.addData( value.toUtf8() );
QString hexData = md5.result();
uint returnval = 0;
foreach( uint val, hexData.toUcs4() )
returnval += val;
return returnval;
}
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoGenericMap );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCustomDataHash );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCustomData );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoCacheCriteria );
#endif // TOMAHAWK_INFOSYSTEM_H

View File

@@ -40,6 +40,7 @@
#include "network/servent.h"
#include "utils/tomahawkutils.h"
#include "kdsingleapplicationguard/kdsingleapplicationguard.h"
class AudioEngine;
class Database;
@@ -98,9 +99,11 @@ public:
// because QApplication::arguments() is expensive
bool scrubFriendlyName() const { return m_scrubFriendlyName; }
public slots:
void instanceStarted( KDSingleApplicationGuard::Instance );
private slots:
void setupSIP();
void messageReceived( const QString& );
private:
void initLocalCollection();

1484
lang/tomahawk_de.ts Normal file

File diff suppressed because it is too large Load Diff

5
lang/tomahawk_i18n.qrc Normal file
View File

@@ -0,0 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/lang">
<file>tomahawk_de.qm</file>
</qresource>
</RCC>

25
lang/translations.cmake Normal file
View File

@@ -0,0 +1,25 @@
FILE (GLOB TS_FILES ${CMAKE_SOURCE_DIR}/lang/*.ts)
QT4_ADD_TRANSLATION(QM_FILES ${TS_FILES})
## HACK HACK HACK - around rcc limitations to allow out of source-tree building
SET( trans_file tomahawk_i18n )
SET( trans_srcfile ${CMAKE_SOURCE_DIR}/lang/${trans_file}.qrc)
SET( trans_infile ${CMAKE_CURRENT_BINARY_DIR}/${trans_file}.qrc)
SET( trans_outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${trans_file}.cxx)
# Copy the QRC file to the output directory
ADD_CUSTOM_COMMAND(
OUTPUT ${trans_infile}
COMMAND ${CMAKE_COMMAND} -E copy ${trans_srcfile} ${trans_infile}
MAIN_DEPENDENCY ${trans_srcfile}
)
# Run the resource compiler (rcc_options should already be set)
ADD_CUSTOM_COMMAND(
OUTPUT ${trans_outfile}
COMMAND ${QT_RCC_EXECUTABLE}
ARGS ${rcc_options} -name ${trans_file} -o ${trans_outfile} ${trans_infile}
MAIN_DEPENDENCY ${trans_infile}
DEPENDS ${QM_FILES}
)

View File

@@ -186,7 +186,9 @@ qt4_wrap_cpp( tomahawkMoc ${tomahawkHeaders} )
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
${CMAKE_CURRENT_BINARY_DIR}/config.h)
SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} )
include( ${CMAKE_SOURCE_DIR}/lang/translations.cmake )
SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} ${trans_outfile})
IF( "${gui}" STREQUAL "no" )
ELSE()

View File

@@ -168,8 +168,8 @@ AudioControls::AudioControls( QWidget* parent )
.scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
connect( TomahawkApp::instance()->infoSystem(),
SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ),
SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ) );
SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ),
SLOT( infoSystemInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ) );
connect( TomahawkApp::instance()->infoSystem(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) );
@@ -252,17 +252,17 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result )
QString artistName = result->artist()->name();
QString albumName = result->album()->name();
Tomahawk::InfoSystem::InfoCustomDataHash trackInfo;
Tomahawk::InfoSystem::InfoCustomData trackInfo;
trackInfo["artist"] = QVariant::fromValue< QString >( result->artist()->name() );
trackInfo["album"] = QVariant::fromValue< QString >( result->album()->name() );
TomahawkApp::instance()->infoSystem()->getInfo(
s_infoIdentifier, Tomahawk::InfoSystem::InfoAlbumCoverArt,
QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomDataHash >( trackInfo ), Tomahawk::InfoSystem::InfoCustomDataHash() );
QVariant::fromValue< Tomahawk::InfoSystem::InfoCustomData >( trackInfo ), Tomahawk::InfoSystem::InfoCustomData() );
}
void
AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData )
AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData )
{
qDebug() << Q_FUNC_INFO;
if ( caller != s_infoIdentifier || type != Tomahawk::InfoSystem::InfoAlbumCoverArt )
@@ -277,13 +277,13 @@ AudioControls::infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType ty
return;
}
if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomDataHash >() )
if ( !output.canConvert< Tomahawk::InfoSystem::InfoCustomData >() )
{
qDebug() << "Cannot convert fetched art from a QByteArray";
return;
}
Tomahawk::InfoSystem::InfoCustomDataHash returnedData = output.value< Tomahawk::InfoSystem::InfoCustomDataHash >();
Tomahawk::InfoSystem::InfoCustomData returnedData = output.value< Tomahawk::InfoSystem::InfoCustomData >();
const QByteArray ba = returnedData["imgbytes"].toByteArray();
if ( ba.length() )
{

View File

@@ -45,7 +45,7 @@ signals:
public slots:
void onRepeatModeChanged( PlaylistInterface::RepeatMode mode );
void onShuffleModeChanged( bool enabled );
void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData );
void infoSystemInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData );
void infoSystemFinished( QString target );
protected:

View File

@@ -21,14 +21,14 @@
#ifdef ENABLE_HEADLESS
#define TOMAHAWK_APPLICATION QtSingleCoreApplication
#define TOMAHAWK_APPLICATION QCoreApplication
#define TOMAHAWK_HEADLESS
#include "qtsingleapp/qtsingleapplication.h"
#include <QApplication>>
#else
#define TOMAHAWK_APPLICATION QtSingleApplication
#include "qtsingleapp/qtsingleapplication.h"
#define TOMAHAWK_APPLICATION QApplication
#include <QApplication>
#include "tomahawkwindow.h"
#endif

View File

@@ -41,7 +41,7 @@ EchoNestPlugin::~EchoNestPlugin()
qDebug() << Q_FUNC_INFO;
}
void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData)
void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomData customData)
{
switch (type)
{
@@ -65,7 +65,7 @@ void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const Q
}
}
void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, InfoCustomDataHash &customData, const QString &item)
void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, InfoCustomData &customData, const QString &item)
{
//WARNING: Totally not implemented yet
@@ -80,7 +80,7 @@ void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data,
// connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot()));
}
void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& data, InfoCustomDataHash &customData)
void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& data, InfoCustomData &customData)
{
if( !isValidArtistData( caller, data, customData ) )
return;
@@ -94,7 +94,7 @@ void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& d
connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot()));
}
void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& data, InfoCustomDataHash &customData)
void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& data, InfoCustomData &customData)
{
if( !isValidArtistData( caller, data, customData ) )
return;
@@ -109,7 +109,7 @@ void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant&
connect(reply, SIGNAL(finished()), SLOT(getArtistFamiliaritySlot()));
}
void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& data, InfoCustomDataHash &customData)
void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& data, InfoCustomData &customData)
{
if( !isValidArtistData( caller, data, customData ) )
return;
@@ -123,7 +123,7 @@ void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant&
connect(reply, SIGNAL(finished()), SLOT(getArtistHotttnesssSlot()));
}
void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, InfoCustomDataHash &customData)
void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, InfoCustomData &customData)
{
if( !isValidArtistData( caller, data, customData ) )
return;
@@ -137,7 +137,7 @@ void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data,
connect(reply, SIGNAL(finished()), SLOT(getArtistTermsSlot()));
}
void EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant& data, InfoCustomDataHash& customData)
void EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant& data, InfoCustomData& customData)
{
QNetworkReply* reply = Echonest::Artist::topTerms( 20 );
m_replyMap[reply] = customData;
@@ -163,7 +163,6 @@ void EchoNestPlugin::getArtistBiographySlot()
}
emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography, reply->property( "data" ), QVariant::fromValue<Tomahawk::InfoSystem::InfoGenericMap>(biographyMap), m_replyMap[reply] );
emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography);
m_replyMap.remove(reply);
m_callerMap.remove(reply);
reply->deleteLater();
@@ -175,7 +174,6 @@ void EchoNestPlugin::getArtistFamiliaritySlot()
Echonest::Artist artist = artistFromReply( reply );
qreal familiarity = artist.familiarity();
emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity, reply->property( "data" ), familiarity, m_replyMap[reply] );
emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity);
m_replyMap.remove(reply);
m_callerMap.remove(reply);
reply->deleteLater();
@@ -187,7 +185,6 @@ void EchoNestPlugin::getArtistHotttnesssSlot()
Echonest::Artist artist = artistFromReply( reply );
qreal hotttnesss = artist.hotttnesss();
emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness, reply->property( "data" ), hotttnesss, m_replyMap[reply] );
emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness);
m_replyMap.remove(reply);
m_callerMap.remove(reply);
reply->deleteLater();
@@ -206,7 +203,6 @@ void EchoNestPlugin::getArtistTermsSlot()
termsMap[ term.name() ] = termMap;
}
emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms, reply->property( "data" ), QVariant::fromValue<Tomahawk::InfoSystem::InfoGenericMap>(termsMap), m_replyMap[reply] );
emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms);
m_replyMap.remove(reply);
m_callerMap.remove(reply);
reply->deleteLater();
@@ -224,13 +220,12 @@ void EchoNestPlugin::getMiscTopSlot()
termsMap[ term.name().toLower() ] = termMap;
}
emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms, QVariant(), QVariant::fromValue<Tomahawk::InfoSystem::InfoGenericMap>(termsMap), m_replyMap[reply] );
emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms);
m_replyMap.remove(reply);
m_callerMap.remove(reply);
reply->deleteLater();
}
bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData)
bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& data, InfoCustomData &customData)
{
if (data.isNull() || !data.isValid() || !data.canConvert<QString>())
{
@@ -246,7 +241,7 @@ bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& da
return true;
}
bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData)
bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& data, InfoCustomData &customData)
{
if (data.isNull() || !data.isValid() || !data.canConvert<QString>())
{

View File

@@ -42,18 +42,18 @@ public:
EchoNestPlugin(QObject *parent);
virtual ~EchoNestPlugin();
void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData );
void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData );
private:
void getSongProfile( const QString &caller, const QVariant &data, InfoCustomDataHash &customData, const QString &item = QString() );
void getArtistBiography ( const QString &caller, const QVariant &data, InfoCustomDataHash &customData );
void getArtistFamiliarity( const QString &caller, const QVariant &data, InfoCustomDataHash &customData );
void getArtistHotttnesss( const QString &caller, const QVariant &data, InfoCustomDataHash &customData );
void getArtistTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData );
void getMiscTopTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData );
void getSongProfile( const QString &caller, const QVariant &data, InfoCustomData &customData, const QString &item = QString() );
void getArtistBiography ( const QString &caller, const QVariant &data, InfoCustomData &customData );
void getArtistFamiliarity( const QString &caller, const QVariant &data, InfoCustomData &customData );
void getArtistHotttnesss( const QString &caller, const QVariant &data, InfoCustomData &customData );
void getArtistTerms( const QString &caller, const QVariant &data, InfoCustomData &customData );
void getMiscTopTerms( const QString &caller, const QVariant &data, InfoCustomData &customData );
bool isValidArtistData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData );
bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData );
bool isValidArtistData( const QString &caller, const QVariant& data, InfoCustomData& customData );
bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomData& customData );
Echonest::Artist artistFromReply( QNetworkReply* );
private slots:
@@ -64,7 +64,7 @@ private slots:
void getMiscTopSlot();
private:
QHash< QNetworkReply*, InfoCustomDataHash > m_replyMap;
QHash< QNetworkReply*, InfoCustomData > m_replyMap;
QHash< QNetworkReply*, QString > m_callerMap;
};

View File

@@ -92,15 +92,14 @@ LastFmPlugin::~LastFmPlugin()
}
void
LastFmPlugin::dataError( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData )
LastFmPlugin::dataError( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData )
{
emit info( caller, type, data, QVariant(), customData );
emit finished( caller, type );
return;
}
void
LastFmPlugin::getInfo( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash customData )
LastFmPlugin::getInfo( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData customData )
{
qDebug() << Q_FUNC_INFO;
if ( type == InfoMiscSubmitNowPlaying )
@@ -114,14 +113,14 @@ LastFmPlugin::getInfo( const QString &caller, const InfoType type, const QVarian
}
void
LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData )
LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData )
{
if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomDataHash >() || !m_scrobbler )
if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomData >() || !m_scrobbler )
{
dataError( caller, type, data, customData );
return;
}
InfoCustomDataHash hash = data.value< Tomahawk::InfoSystem::InfoCustomDataHash >();
InfoCustomData hash = data.value< Tomahawk::InfoSystem::InfoCustomData >();
if ( !hash.contains( "title" ) || !hash.contains( "artist" ) || !hash.contains( "album" ) || !hash.contains( "duration" ) )
{
dataError( caller, type, data, customData );
@@ -139,11 +138,10 @@ LastFmPlugin::nowPlaying( const QString &caller, const InfoType type, const QVar
m_scrobbler->nowPlaying( m_track );
emit info( caller, type, data, QVariant(), customData );
emit finished( caller, type );
}
void
LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData )
LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData )
{
Q_ASSERT( QThread::currentThread() == thread() );
@@ -158,37 +156,53 @@ LastFmPlugin::scrobble( const QString &caller, const InfoType type, const QVaria
m_scrobbler->submit();
emit info( caller, type, data, QVariant(), customData );
emit finished( caller, type );
}
void
LastFmPlugin::fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData )
LastFmPlugin::fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData )
{
qDebug() << Q_FUNC_INFO;
if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomDataHash >() )
if ( !data.canConvert< Tomahawk::InfoSystem::InfoCustomData >() )
{
dataError( caller, type, data, customData );
return;
}
InfoCustomDataHash hash = data.value< Tomahawk::InfoSystem::InfoCustomDataHash >();
InfoCustomData hash = data.value< Tomahawk::InfoSystem::InfoCustomData >();
if ( !hash.contains( "artist" ) || !hash.contains( "album" ) )
{
dataError( caller, type, data, customData );
return;
}
QString artistName = hash["artist"].toString();
QString albumName = hash["album"].toString();
Tomahawk::InfoSystem::InfoCacheCriteria criteria;
criteria["artist"] = hash["artist"].toString();
criteria["album"] = hash["album"].toString();
emit getCachedInfo( criteria, caller, type, data, customData );
}
void
LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData )
{
qDebug() << Q_FUNC_INFO;
if ( type == InfoAlbumCoverArt )
{
QString artistName = criteria["artist"];
QString albumName = criteria["album"];
QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&size=medium&api_key=7a90f6672a04b809ee309af169f34b8b";
QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) );
QNetworkReply* reply = TomahawkUtils::nam()->get( req );
reply->setProperty("customData", QVariant::fromValue<Tomahawk::InfoSystem::InfoCustomDataHash>(customData));
reply->setProperty("origData", data);
reply->setProperty( "customData", QVariant::fromValue<Tomahawk::InfoSystem::InfoCustomData>( customData ) );
reply->setProperty( "origData", input );
reply->setProperty( "caller", caller );
reply->setProperty( "type", (uint)(type) );
connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) );
return;
}
else
qDebug() << "Couldn't figure out what to do with this type of request after cache miss";
}
void
@@ -200,17 +214,25 @@ LastFmPlugin::coverArtReturned()
if ( redir.isEmpty() )
{
const QByteArray ba = reply->readAll();
Tomahawk::InfoSystem::InfoCustomDataHash returnedData;
InfoCustomData returnedData;
returnedData["imgbytes"] = ba;
returnedData["url"] = reply->url().toString();
InfoCustomData customData = reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomData >();
InfoType type = (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt());
emit info(
reply->property( "caller" ).toString(),
(Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()),
type,
reply->property( "origData" ),
returnedData,
reply->property( "customData" ).value< Tomahawk::InfoSystem::InfoCustomDataHash >()
customData
);
emit finished( reply->property( "caller" ).toString(), (Tomahawk::InfoSystem::InfoType)(reply->property( "type" ).toUInt()) );
InfoCustomData origData = reply->property( "origData" ).value< Tomahawk::InfoSystem::InfoCustomData >();
Tomahawk::InfoSystem::InfoCacheCriteria criteria;
criteria["artist"] = origData["artist"].toString();
criteria["album"] = origData["album"].toString();
emit updateCache( criteria, type, returnedData );
}
else
{

View File

@@ -43,19 +43,20 @@ public:
LastFmPlugin( QObject *parent );
virtual ~LastFmPlugin();
void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData );
void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData );
public slots:
void settingsChanged();
void onAuthenticated();
void coverArtReturned();
virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData );
private:
void fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData );
void scrobble( const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash &customData );
void fetchCoverArt( const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData );
void scrobble( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData );
void createScrobbler();
void nowPlaying( const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash &customData );
void dataError( const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash &customData );
void nowPlaying( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData );
void dataError( const QString &caller, const InfoType type, const QVariant& data, InfoCustomData &customData );
lastfm::MutableTrack m_track;
lastfm::Audioscrobbler* m_scrobbler;

View File

@@ -42,18 +42,17 @@ MusixMatchPlugin::~MusixMatchPlugin()
qDebug() << Q_FUNC_INFO;
}
void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash customData)
void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData customData)
{
qDebug() << Q_FUNC_INFO;
if( !isValidTrackData(caller, data, customData) || !data.canConvert<Tomahawk::InfoSystem::InfoCustomDataHash>())
if( !isValidTrackData(caller, data, customData) || !data.canConvert<Tomahawk::InfoSystem::InfoCustomData>())
return;
Tomahawk::InfoSystem::InfoCustomDataHash hash = data.value<Tomahawk::InfoSystem::InfoCustomDataHash>();
Tomahawk::InfoSystem::InfoCustomData hash = data.value<Tomahawk::InfoSystem::InfoCustomData>();
QString artist = hash["artistName"].toString();
QString track = hash["trackName"].toString();
if( artist.isEmpty() || track.isEmpty() )
{
emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData);
emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics);
return;
}
qDebug() << "artist is " << artist << ", track is " << track;
@@ -63,35 +62,32 @@ void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const
url.addQueryItem("q_artist", artist);
url.addQueryItem("q_track", track);
QNetworkReply* reply = TomahawkUtils::nam()->get(QNetworkRequest(url));
reply->setProperty("customData", QVariant::fromValue<Tomahawk::InfoSystem::InfoCustomDataHash>(customData));
reply->setProperty("customData", QVariant::fromValue<Tomahawk::InfoSystem::InfoCustomData>(customData));
reply->setProperty("origData", data);
reply->setProperty("caller", caller);
connect(reply, SIGNAL(finished()), SLOT(trackSearchSlot()));
}
bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData)
bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& data, Tomahawk::InfoSystem::InfoCustomData &customData)
{
qDebug() << Q_FUNC_INFO;
if (data.isNull() || !data.isValid() || !data.canConvert<Tomahawk::InfoSystem::InfoCustomDataHash>())
if (data.isNull() || !data.isValid() || !data.canConvert<Tomahawk::InfoSystem::InfoCustomData>())
{
emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData);
emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics);
qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert";
return false;
}
InfoCustomDataHash hash = data.value<Tomahawk::InfoSystem::InfoCustomDataHash>();
InfoCustomData hash = data.value<Tomahawk::InfoSystem::InfoCustomData>();
if (hash["trackName"].toString().isEmpty() )
{
emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData);
emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics);
qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty";
return false;
}
if (hash["artistName"].toString().isEmpty() )
{
emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData);
emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics);
qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found";
return false;
}
@@ -104,7 +100,7 @@ void MusixMatchPlugin::trackSearchSlot()
QNetworkReply* oldReply = qobject_cast<QNetworkReply*>( sender() );
if (!oldReply)
{
emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash());
emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomData());
return;
}
QDomDocument doc;
@@ -113,8 +109,7 @@ void MusixMatchPlugin::trackSearchSlot()
QDomNodeList domNodeList = doc.elementsByTagName("track_id");
if (domNodeList.isEmpty())
{
emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value<Tomahawk::InfoSystem::InfoCustomDataHash>());
emit finished(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics);
emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value<Tomahawk::InfoSystem::InfoCustomData>());
return;
}
QString track_id = domNodeList.at(0).toElement().text();
@@ -135,7 +130,7 @@ void MusixMatchPlugin::trackLyricsSlot()
QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
if (!reply)
{
emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash());
emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomData());
return;
}
QDomDocument doc;
@@ -143,12 +138,10 @@ void MusixMatchPlugin::trackLyricsSlot()
QDomNodeList domNodeList = doc.elementsByTagName("lyrics_body");
if (domNodeList.isEmpty())
{
emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value<Tomahawk::InfoSystem::InfoCustomDataHash>());
emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics);
emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value<Tomahawk::InfoSystem::InfoCustomData>());
return;
}
QString lyrics = domNodeList.at(0).toElement().text();
qDebug() << "Emitting lyrics: " << lyrics;
emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value<Tomahawk::InfoSystem::InfoCustomDataHash>());
emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics);
emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value<Tomahawk::InfoSystem::InfoCustomData>());
}

View File

@@ -36,10 +36,10 @@ public:
MusixMatchPlugin(QObject *parent);
virtual ~MusixMatchPlugin();
void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData);
void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomData customData);
private:
bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash &customData );
bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomData &customData );
public slots:
void trackSearchSlot();

View File

@@ -25,7 +25,11 @@
#include "infoplugins/musixmatchplugin.h"
#include "infoplugins/lastfmplugin.h"
using namespace Tomahawk::InfoSystem;
namespace Tomahawk
{
namespace InfoSystem
{
InfoPlugin::InfoPlugin(QObject *parent)
:QObject( parent )
@@ -33,11 +37,26 @@ InfoPlugin::InfoPlugin(QObject *parent)
qDebug() << Q_FUNC_INFO;
InfoSystem *system = qobject_cast< InfoSystem* >( parent );
if( system )
QObject::connect( system->getCache(),
SIGNAL( notInCache( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) ),
{
QObject::connect(
this,
SLOT( notInCacheSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash ) )
SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ),
system->getCache(),
SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) )
);
QObject::connect(
system->getCache(),
SIGNAL( notInCache( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ),
this,
SLOT( notInCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria, QString, Tomahawk::InfoSystem::InfoType, QVariant, Tomahawk::InfoSystem::InfoCustomData ) )
);
QObject::connect(
this,
SIGNAL( updateCache( Tomahawk::InfoSystem::InfoCacheCriteria, Tomahawk::InfoSystem::InfoType, QVariant ) ),
system->getCache(),
SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria, Tomahawk::InfoSystem::InfoType, QVariant ) )
);
}
}
@@ -46,10 +65,12 @@ InfoSystem::InfoSystem(QObject *parent)
{
qDebug() << Q_FUNC_INFO;
qRegisterMetaType< QMap< QString, QMap< QString, QString > > >( "Tomahawk::InfoSystem::InfoGenericMap" );
qRegisterMetaType<QHash<QString, QVariant > >("Tomahawk::InfoSystem::InfoCustomDataHash");
qRegisterMetaType< QHash< QString, QVariant > >( "Tomahawk::InfoSystem::InfoCustomData" );
qRegisterMetaType< QHash< QString, QString > >( "Tomahawk::InfoSystem::InfoCacheCriteria" );
qRegisterMetaType< Tomahawk::InfoSystem::InfoType >( "Tomahawk::InfoSystem::InfoType" );
m_infoSystemCacheThreadController = new QThread( this );
m_cache = new Tomahawk::InfoSystem::InfoSystemCache();
m_cache = new InfoSystemCache();
m_cache->moveToThread( m_infoSystemCacheThreadController );
m_infoSystemCacheThreadController->start( QThread::IdlePriority );
@@ -59,6 +80,19 @@ InfoSystem::InfoSystem(QObject *parent)
m_plugins.append( mmptr );
InfoPluginPtr lfmptr( new LastFmPlugin( this ) );
m_plugins.append( lfmptr );
Q_FOREACH( InfoPluginPtr plugin, m_plugins )
{
connect(
plugin.data(),
SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ),
this,
SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ),
Qt::UniqueConnection
);
}
connect( m_cache, SIGNAL( info( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ),
this, SLOT( infoSlot( QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomData ) ), Qt::UniqueConnection );
}
InfoSystem::~InfoSystem()
@@ -108,13 +142,13 @@ QLinkedList< InfoPluginPtr > InfoSystem::determineOrderedMatches(const InfoType
return providers;
}
void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData)
void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomData customData)
{
qDebug() << Q_FUNC_INFO;
QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type);
if (providers.isEmpty())
{
emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData);
emit info(QString(), InfoNoInfo, QVariant(), QVariant(), customData);
emit finished(caller);
return;
}
@@ -122,27 +156,23 @@ void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVari
InfoPluginPtr ptr = providers.first();
if (!ptr)
{
emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData);
emit info(QString(), InfoNoInfo, QVariant(), QVariant(), customData);
emit finished(caller);
return;
}
m_dataTracker[caller][type] = m_dataTracker[caller][type] + 1;
qDebug() << "current count in dataTracker for type" << type << "is" << m_dataTracker[caller][type];
connect(ptr.data(), SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)),
this, SLOT(infoSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), Qt::UniqueConnection);
connect(ptr.data(), SIGNAL(finished(QString, Tomahawk::InfoSystem::InfoType)),
this, SLOT(finishedSlot(QString, Tomahawk::InfoSystem::InfoType)), Qt::UniqueConnection);
ptr.data()->getInfo(caller, type, data, customData);
}
void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomDataHash customData)
void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomData customData)
{
Q_FOREACH( InfoType type, input.keys() )
getInfo(caller, type, input[type], customData);
}
void InfoSystem::infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData)
void InfoSystem::infoSlot(QString target, InfoType type, QVariant input, QVariant output, InfoCustomData customData)
{
qDebug() << Q_FUNC_INFO;
qDebug() << "current count in dataTracker is " << m_dataTracker[target][type];
@@ -152,14 +182,10 @@ void InfoSystem::infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, Q
return;
}
emit info(target, type, input, output, customData);
}
void InfoSystem::finishedSlot(QString target, Tomahawk::InfoSystem::InfoType type)
{
qDebug() << Q_FUNC_INFO;
m_dataTracker[target][type] = m_dataTracker[target][type] - 1;
qDebug() << "current count in dataTracker is " << m_dataTracker[target][type];
Q_FOREACH(Tomahawk::InfoSystem::InfoType testtype, m_dataTracker[target].keys())
Q_FOREACH(InfoType testtype, m_dataTracker[target].keys())
{
if (m_dataTracker[target][testtype] != 0)
{
@@ -170,3 +196,7 @@ void InfoSystem::finishedSlot(QString target, Tomahawk::InfoSystem::InfoType typ
qDebug() << "emitting finished with target" << target;
emit finished(target);
}
} //namespace InfoSystem
} //namespace Tomahawk

View File

@@ -0,0 +1,45 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QtDebug>
#include "infosystemcache.h"
void
Tomahawk::InfoSystem::InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData )
{
qDebug() << Q_FUNC_INFO;
if( !m_memCache.contains( type ) || !m_memCache[type].contains( criteria ) )
{
emit notInCache( criteria, caller, type, input, customData );
return;
}
emit info( caller, type, input, m_memCache[type][criteria], customData );
}
void
Tomahawk::InfoSystem::InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output )
{
qDebug() << Q_FUNC_INFO;
QHash< InfoCacheCriteria, QVariant > typecache;
if( m_memCache.contains( type ) )
typecache = m_memCache[type];
typecache[criteria] = output;
m_memCache[type] = typecache;
}

View File

@@ -22,6 +22,8 @@
#include <QObject>
#include <QtDebug>
#include "tomahawk/infosystem.h"
namespace Tomahawk
{
@@ -44,6 +46,16 @@ public:
qDebug() << Q_FUNC_INFO;
}
signals:
void notInCache( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData );
void info( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomData customData );
public slots:
void getCachedInfoSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, Tomahawk::InfoSystem::InfoCustomData customData );
void updateCacheSlot( Tomahawk::InfoSystem::InfoCacheCriteria criteria, Tomahawk::InfoSystem::InfoType type, QVariant output );
private:
QHash< InfoType, QHash< InfoCacheCriteria, QVariant > > m_memCache;
};
} //namespace InfoSystem

View File

@@ -146,8 +146,10 @@ set( libSources
widgets/overlaywidget.cpp
widgets/infowidgets/sourceinfowidget.cpp
qtsingleapp/qtlocalpeer.cpp
qtsingleapp/qtsingleapplication.cpp
kdsingleapplicationguard/kdsingleapplicationguard.cpp
kdsingleapplicationguard/kdsharedmemorylocker.cpp
kdsingleapplicationguard/kdtoolsglobal.cpp
kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp
)
set( libHeaders
@@ -289,8 +291,10 @@ set( libHeaders
widgets/overlaywidget.h
widgets/infowidgets/sourceinfowidget.h
qtsingleapp/qtlocalpeer.h
qtsingleapp/qtsingleapplication.h
kdsingleapplicationguard/kdsingleapplicationguard.h
kdsingleapplicationguard/kdsharedmemorylocker.h
kdsingleapplicationguard/kdtoolsglobal.h
kdsingleapplicationguard/kdlockedsharedmemorypointer.h
)
set( libHeaders_NoMOC

View File

@@ -124,7 +124,7 @@ void
ACLSystem::authorizePath( const QString& dbid, const QString& path, ACLSystem::ACL type )
{
TomahawkSettings *s = TomahawkSettings::instance();
if( !s->scannerPath().contains( path ) )
if( !s->scannerPaths().contains( path ) )
{
qDebug() << "path selected is not in our scanner path!";
return;

View File

@@ -32,11 +32,16 @@ Database::instance()
Database::Database( const QString& dbname, QObject* parent )
: QObject( parent )
, m_ready( false )
, m_impl( new DatabaseImpl( dbname, this ) )
, m_workerRW( new DatabaseWorker( m_impl, this, true ) )
{
s_instance = this;
connect( m_impl, SIGNAL( indexReady() ), SIGNAL( indexReady() ) );
connect( m_impl, SIGNAL( indexReady() ), SIGNAL( ready() ) );
connect( m_impl, SIGNAL( indexReady() ), SLOT( setIsReadyTrue() ) );
m_workerRW->start();
}

View File

@@ -41,6 +41,7 @@
class DLLEXPORT Database : public QObject
{
Q_OBJECT
public:
static Database* instance();
@@ -52,15 +53,23 @@ public:
void loadIndex();
bool isReady() const { return m_ready; }
signals:
void indexReady(); // search index
void ready();
void newJobRO( QSharedPointer<DatabaseCommand> );
void newJobRW( QSharedPointer<DatabaseCommand> );
public slots:
void enqueue( QSharedPointer<DatabaseCommand> lc );
private slots:
void setIsReadyTrue() { m_ready = true; }
private:
bool m_ready;
DatabaseImpl* m_impl;
DatabaseWorker* m_workerRW;
QHash< QString, DatabaseWorker* > m_workers;

View File

@@ -45,6 +45,12 @@ public:
setSource( source );
}
explicit DatabaseCommand_DeleteFiles( const QVariantList& ids, const Tomahawk::source_ptr& source, QObject* parent = 0 )
: DatabaseCommandLoggable( parent ), m_ids( ids )
{
setSource( source );
}
virtual QString commandname() const { return "deletefiles"; }
virtual void exec( DatabaseImpl* );

View File

@@ -38,21 +38,35 @@ DatabaseCommand_DirMtimes::execSelect( DatabaseImpl* dbi )
{
QMap<QString,unsigned int> mtimes;
TomahawkSqlQuery query = dbi->newquery();
if( m_prefix.isEmpty() )
if( m_prefix.isEmpty() && m_prefixes.isEmpty() )
query.exec( "SELECT name, mtime FROM dirs_scanned" );
else if( m_prefixes.isEmpty() )
execSelectPath( dbi, m_prefix, mtimes );
else
{
if( !m_prefix.isEmpty() )
execSelectPath( dbi, m_prefix, mtimes );
foreach( QString path, m_prefixes )
execSelectPath( dbi, path, mtimes );
}
emit done( mtimes );
}
void
DatabaseCommand_DirMtimes::execSelectPath( DatabaseImpl *dbi, const QDir& path, QMap<QString, unsigned int> &mtimes )
{
TomahawkSqlQuery query = dbi->newquery();
query.prepare( QString( "SELECT name, mtime "
"FROM dirs_scanned "
"WHERE name LIKE '%1%'" ).arg( m_prefix.replace( '\'',"''" ) ) );
"WHERE name LIKE :prefix" ) );
query.bindValue( ":prefix", path.absolutePath() + "%" );
query.exec();
}
while( query.next() )
{
mtimes.insert( query.value( 0 ).toString(), query.value( 1 ).toUInt() );
}
emit done( mtimes );
}

View File

@@ -34,10 +34,14 @@ class DLLEXPORT DatabaseCommand_DirMtimes : public DatabaseCommand
Q_OBJECT
public:
explicit DatabaseCommand_DirMtimes( const QString& prefix = "", QObject* parent = 0 )
explicit DatabaseCommand_DirMtimes( const QString& prefix = QString(), QObject* parent = 0 )
: DatabaseCommand( parent ), m_prefix( prefix ), m_update( false )
{}
explicit DatabaseCommand_DirMtimes( const QStringList& prefixes = QStringList(), QObject* parent = 0 )
: DatabaseCommand( parent ), m_prefixes( prefixes ), m_update( false )
{}
explicit DatabaseCommand_DirMtimes( QMap<QString, unsigned int> tosave, QObject* parent = 0 )
: DatabaseCommand( parent ), m_update( true ), m_tosave( tosave )
{}
@@ -52,9 +56,12 @@ signals:
public slots:
private:
void execSelectPath( DatabaseImpl *dbi, const QDir& path, QMap<QString, unsigned int> &mtimes );
void execSelect( DatabaseImpl* dbi );
void execUpdate( DatabaseImpl* dbi );
QString m_prefix;
QStringList m_prefixes;
bool m_update;
QMap<QString, unsigned int> m_tosave;
};

View File

@@ -52,9 +52,9 @@ DatabaseCommand_LoadDynamicPlaylist::exec( DatabaseImpl* dbi )
QList< QVariantMap > controls;
QString playlist_guid;
qDebug() << "Loading controls..." << revisionGuid();
qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype "
"FROM dynamic_playlist_revision, playlist_revision "
"WHERE dynamic_playlist_revision.guid = "<< revisionGuid() << " AND playlist_revision.guid = dynamic_playlist_revision.guid";
// qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype "
// "FROM dynamic_playlist_revision, playlist_revision "
// "WHERE dynamic_playlist_revision.guid = "<< revisionGuid() << " AND playlist_revision.guid = dynamic_playlist_revision.guid";
if( controlsQuery.first() )
{
playlist_guid = controlsQuery.value( 0 ).toString();

View File

@@ -45,7 +45,7 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi )
query_entries.bindValue( ":guid", m_revguid );
query_entries.exec();
qDebug() << "trying to load entries:" << m_revguid;
// qDebug() << "trying to load entries:" << m_revguid;
QString prevrev;
QJson::Parser parser; bool ok;
@@ -55,7 +55,6 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi )
QVariant v = parser.parse( query_entries.value(0).toByteArray(), &ok );
Q_ASSERT( ok && v.type() == QVariant::List ); //TODO
m_guids = v.toStringList();
// qDebug() << "Entries:" << guids;
QString inclause = QString("('%1')").arg(m_guids.join("', '"));
@@ -115,5 +114,5 @@ DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi )
m_islatest = query_entries_old.value( 1 ).toBool();
}
qDebug() << Q_FUNC_INFO << "entrymap:" << m_entrymap;
// qDebug() << Q_FUNC_INFO << "entrymap:" << m_entrymap;
}

View File

@@ -44,7 +44,8 @@ DatabaseCommand_LogPlayback::postCommitHook()
connect( this, SIGNAL( trackPlayed( Tomahawk::query_ptr ) ),
source().data(), SLOT( onPlaybackFinished( Tomahawk::query_ptr ) ), Qt::QueuedConnection );
Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString(), uuid() );
// do not auto resolve this track
Tomahawk::query_ptr q = Tomahawk::Query::get( m_artist, m_track, QString() );
if ( m_action == Finished )
{

View File

@@ -46,8 +46,6 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent )
, m_lastalbid( 0 )
, m_lasttrkid( 0 )
{
connect( this, SIGNAL( indexReady() ), parent, SIGNAL( indexReady() ) );
db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" );
db.setDatabaseName( dbname );
if ( !db.open() )

View File

@@ -84,6 +84,8 @@ signals:
public slots:
private:
bool m_ready;
bool updateSchema( int currentver );
QSqlDatabase db;

View File

@@ -0,0 +1,475 @@
#include "kdlockedsharedmemorypointer.h"
#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN )
#ifndef QT_NO_SHAREDMEMORY
namespace kdtools
{
}
using namespace kdtools;
KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory * m )
: locker( m ),
mem( m )
{
}
KDLockedSharedMemoryPointerBase::KDLockedSharedMemoryPointerBase( QSharedMemory & m )
: locker( &m ),
mem( &m )
{
}
KDLockedSharedMemoryPointerBase::~KDLockedSharedMemoryPointerBase() {}
void * KDLockedSharedMemoryPointerBase::get() {
return mem ? mem->data() : 0 ;
}
const void * KDLockedSharedMemoryPointerBase::get() const {
return mem ? mem->data() : 0 ;
}
size_t KDLockedSharedMemoryPointerBase::byteSize() const {
return mem->size();
}
/*!
\class KDLockedSharedMemoryPointer
\ingroup core raii smartptr
\brief Locking pointer for Qt shared memory segments
\since_c 2.1
(The exception safety of this class has not been evaluated yet.)
KDLockedSharedMemoryPointer is a smart immutable pointer, which gives convenient and safe access to a QSharedMemory data segment.
The content of a KDLockedSharedMemoryPointer cannot be changed during it's lifetime.
You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it.
\note You can only put simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the
documentation of QSharedMemory for details.
*/
/*!
\fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory * mem )
Constructor. Constructs a KDLockedSharedMemory pointer which points to the data segment of \a mem.
The constructor locks \a mem. If the memory segment is already locked by another process, this constructor
blocks until the lock is released.
\post data() == mem->data() and the memory segment has been locked
*/
/*!
\fn KDLockedSharedMemoryPointer::KDLockedSharedMemoryPointer( QSharedMemory & mem )
\overload
\post data() == mem.data() and the memory segment has been locked
*/
/*!
\fn KDLockedSharedMemoryPointer::~KDLockedSharedMemoryPointer()
Destructor. Unlocks the shared memory segment.
\post The shared memory segment has been unlocked
*/
/*!
\fn T * KDLockedSharedMemoryPointer::get()
\returns a pointer to the contained object.
*/
/*!
\fn const T * KDLockedSharedMemoryPointer::get() const
\returns a const pointer to the contained object
\overload
*/
/*!
\fn T * KDLockedSharedMemoryPointer::data()
Equivalent to get(), provided for consistency with Qt naming conventions.
*/
/*!
\fn const T * KDLockedSharedMemoryPointer::data() const
\overload
*/
/*!
\fn T & KDLockedSharedMemoryPointer::operator*()
Dereference operator. Returns \link get() *get()\endlink.
*/
/*!
\fn const T & KDLockedSharedMemoryPointer::operator*() const
Dereference operator. Returns \link get() *get()\endlink.
\overload
*/
/*!
\fn T * KDLockedSharedMemoryPointer::operator->()
Member-by-pointer operator. Returns get().
*/
/*!
\fn const T * KDLockedSharedMemoryPointer::operator->() const
Member-by-pointer operator. Returns get().
\overload
*/
/*!
\class KDLockedSharedMemoryArray
\ingroup core raii smartptr
\brief Locking array pointer to Qt shared memory segments
\since_c 2.1
(The exception safety of this class has not been evaluated yet.)
KDLockedSharedMemoryArray is a smart immutable pointer, which gives convenient and safe access to array data stored in a QSharedMemory
data segment.
The content of a KDLockedSharedMemoryArray cannot be changed during it's lifetime.
You can use this class like a normal pointer to the shared memory segment and be sure it's locked while accessing it.
\note You can only put arrays of simple types/structs/classes into it. structs and classes shall not contain any other pointers. See the
documentation of QSharedMemory for details.
\sa KDLockedSharedMemoryPointer
*/
/*!
\fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory* mem )
Constructor. Constructs a KDLockedSharedMemoryArray which points to the data segment of \a mem. The constructor locks \a mem. If the memory
segment is already locked by another process, this constructor blocks until the lock is release.
\post get() == mem->data() and the memory segment has been locked
*/
/*!
\fn KDLockedSharedMemoryArray::KDLockedSharedMemoryArray( QSharedMemory& mem )
\overload
\post get() == mem->data() and the memory segment has been locked
*/
/*!
\typedef KDLockedSharedMemoryArray::size_type
Typedef for std::size_t. Provided for STL compatibility.
*/
/*!
\typedef KDLockedSharedMemoryArray::difference_type
Typedef for std::ptrdiff_t. Provided for STL compatibility.
*/
/*!
\typedef KDLockedSharedMemoryArray::iterator
Typedef for T*. Provided for STL compatibility.
\since_t 2.2
*/
/*!
\typedef KDLockedSharedMemoryArray::const_iterator
Typedef for const T*. Provided for STL compatibility.
\since_t 2.2
*/
/*!
\typedef KDLockedSharedMemoryArray::reverse_iterator
Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::iterator iterator\endlink >. Provided for STL compatibility.
\since_t 2.2
*/
/*!
\typedef KDLockedSharedMemoryArray::const_reverse_iterator
Typedef for std::reverse_iterator< \link KDLockedSharedMemoryArray::const_iterator const_iterator\endlink >. Provided for STL compatibility.
\since_t 2.2
*/
/*!
\fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::begin()
Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the first item of the array.
\since_f 2.2
*/
/*!
\fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::begin() const
\overload
\since_f 2.2
*/
/*!
\fn KDLockedSharedMemoryArray::iterator KDLockedSharedMemoryArray::end()
Returns an \link KDLockedSharedMemoryArray::iterator iterator\endlink pointing to the item after the last item of the array.
\since_f 2.2
*/
/*!
\fn KDLockedSharedMemoryArray::const_iterator KDLockedSharedMemoryArray::end() const
\overload
\since_f 2.2
*/
/*!
\fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rbegin()
Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the item after the last item of the array.
\since_f 2.2
*/
/*!
\fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rbegin() const
\overload
\since_f 2.2
*/
/*!
\fn KDLockedSharedMemoryArray::reverse_iterator KDLockedSharedMemoryArray::rend()
Returns an \link KDLockedSharedMemoryArray::reverse_iterator reverse_iterator\endlink pointing to the first item of the array.
\since_f 2.2
*/
/*!
\fn KDLockedSharedMemoryArray::const_reverse_iterator KDLockedSharedMemoryArray::rend() const
\overload
\since_f 2.2
*/
/*!
\fn KDLockedSharedMemoryArray::size_type KDLockedSharedMemoryArray::size() const
Returns the size of this array. The size is calculated from the storage size of T and
the size of the shared memory segment.
\since_f 2.2
*/
/*!
\fn T& KDLockedSharedMemoryArray::operator[]( difference_type n )
Array access operator. Returns a reference to the item at index position \a n.
*/
/*!
\fn const T& KDLockedSharedMemoryArray::operator[]( difference_type n ) const
\overload
*/
/*!
\fn T& KDLockedSharedMemoryArray::front()
Returns a reference to the first item in the array. This is the same as operator[](0).
*/
/*!
\fn const T& KDLockedSharedMemoryArray::front() const
\overload
*/
/*!
\fn T& KDLockedSharedMemoryArray::back()
Returns a reference to the last item in the array. This is the same as operator[](size()-1).
\since_f 2.2
*/
/*!
\fn const T& KDLockedSharedMemoryArray::back() const
\overload
\since_f 2.2
*/
#ifdef eKDTOOLSCORE_UNITTESTS
#include <KDUnitTest/Test>
#include <QThread>
#include <QUuid>
namespace
{
struct TestStruct
{
TestStruct( uint nn = 0 )
: n( nn ),
f( 0.0 ),
c( '\0' ),
b( false )
{
}
uint n;
double f;
char c;
bool b;
};
bool operator==( const TestStruct& lhs, const TestStruct& rhs )
{
return lhs.n == rhs.n && lhs.f == rhs.f && lhs.c == rhs.c && lhs.b == rhs.b;
}
class TestThread : public QThread
{
public:
TestThread( const QString& key )
: mem( key )
{
mem.attach();
}
void run()
{
while( true )
{
msleep( 100 );
kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem );
if( !p->b )
continue;
p->n = 5;
p->f = 3.14;
p->c = 'A';
p->b = false;
return;
}
}
QSharedMemory mem;
};
bool isConst( TestStruct* )
{
return false;
}
bool isConst( const TestStruct* )
{
return true;
}
}
KDAB_UNITTEST_SIMPLE( KDLockedSharedMemoryPointer, "kdcoretools" ) {
const QString key = QUuid::createUuid();
QSharedMemory mem( key );
const bool created = mem.create( sizeof( TestStruct ) );
assertTrue( created );
if ( !created )
return; // don't execute tests if shm coulnd't be created
// On Windows, shared mem is only available in increments of page
// size (4k), so don't fail if the segment is larger:
const unsigned long mem_size = mem.size();
assertGreaterOrEqual( mem_size, sizeof( TestStruct ) );
{
kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem );
assertTrue( p );
*p = TestStruct();
assertEqual( p->n, 0u );
assertEqual( p->f, 0.0 );
assertEqual( p->c, '\0' );
assertFalse( p->b );
}
{
TestThread thread( key );
assertEqual( thread.mem.key().toStdString(), key.toStdString() );
assertEqual( static_cast< unsigned long >( thread.mem.size() ), mem_size );
thread.start();
assertTrue( thread.isRunning() );
thread.wait( 2000 );
assertTrue( thread.isRunning() );
{
kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem );
p->b = true;
}
thread.wait( 2000 );
assertFalse( thread.isRunning() );
}
{
kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem );
assertEqual( p->n, 5u );
assertEqual( p->f, 3.14 );
assertEqual( p->c, 'A' );
assertFalse( p->b );
}
{
kdtools::KDLockedSharedMemoryPointer< TestStruct > p( mem );
assertEqual( mem.data(), p.get() );
assertEqual( p.get(), p.operator->() );
assertEqual( p.get(), &(*p) );
assertEqual( p.get(), p.data() );
assertFalse( isConst( p.get() ) );
}
{
const kdtools::KDLockedSharedMemoryPointer< TestStruct > p( &mem );
assertEqual( mem.data(), p.get() );
assertEqual( p.get(), p.operator->() );
assertEqual( p.get(), &(*p) );
assertEqual( p.get(), p.data() );
assertTrue( isConst( p.get() ) );
}
{
QSharedMemory mem2( key + key );
const bool created2 = mem2.create( 16 * sizeof( TestStruct ) );
assertTrue( created2 );
if ( !created2 )
return; // don't execute tests if shm coulnd't be created
kdtools::KDLockedSharedMemoryArray<TestStruct> a( mem2 );
assertTrue( a );
assertEqual( a.get(), mem2.data() );
assertEqual( &a[0], a.get() );
a[1] = a[0];
assertTrue( a[0] == a[1] );
TestStruct ts;
ts.n = 5;
ts.f = 3.14;
a[0] = ts;
assertFalse( a[0] == a[1] );
assertEqual( a.front().n, ts.n );
assertEqual( a[0].f, ts.f );
a[0].n = 10;
assertEqual( a.front().n, 10u );
ts = a[0];
assertEqual( ts.n, 10u );
std::vector< TestStruct > v;
for( uint i = 0; i < a.size(); ++i )
v.push_back( TestStruct( i ) );
std::copy( v.begin(), v.end(), a.begin() );
for( uint i = 0; i < a.size(); ++i )
assertEqual( a[ i ].n, i );
assertEqual( a.front().n, 0u );
assertEqual( a.back().n, a.size() - 1 );
std::copy( v.begin(), v.end(), a.rbegin() );
for( uint i = 0; i < a.size(); ++i )
assertEqual( a[ i ].n, a.size() - 1 - i );
assertEqual( a.front().n, a.size() - 1 );
assertEqual( a.back().n, 0u );
}
}
#endif // KDTOOLSCORE_UNITTESTS
#endif // QT_NO_SHAREDMEMORY
#endif // QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN )

View File

@@ -0,0 +1,115 @@
#ifndef __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__
#define __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__
#include <QtCore/QtGlobal>
#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN )
#ifndef QT_NO_SHAREDMEMORY
#include "kdsharedmemorylocker.h"
#include <QtCore/QSharedMemory>
#include <cassert>
#ifndef DOXYGEN_RUN
namespace kdtools {
#endif
class KDLockedSharedMemoryPointerBase {
protected:
explicit KDLockedSharedMemoryPointerBase( QSharedMemory * mem );
explicit KDLockedSharedMemoryPointerBase( QSharedMemory & mem );
~KDLockedSharedMemoryPointerBase();
// PENDING(marc) do we really want const propagation here? I
// usually declare all my RAII objects const...
void * get();
const void * get() const;
KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() )
size_t byteSize() const;
private:
KDSharedMemoryLocker locker;
QSharedMemory * const mem;
};
template< typename T>
class MAKEINCLUDES_EXPORT KDLockedSharedMemoryPointer : KDLockedSharedMemoryPointerBase {
KDAB_DISABLE_COPY( KDLockedSharedMemoryPointer );
public:
explicit KDLockedSharedMemoryPointer( QSharedMemory * m )
: KDLockedSharedMemoryPointerBase( m ) {}
explicit KDLockedSharedMemoryPointer( QSharedMemory & m )
: KDLockedSharedMemoryPointerBase( m ) {}
T * get() { return static_cast<T*>( KDLockedSharedMemoryPointerBase::get() ); }
const T * get() const { return static_cast<const T*>( KDLockedSharedMemoryPointerBase::get() ); }
T * data() { return static_cast<T*>( get() ); }
const T * data() const { return static_cast<const T*>( get() ); }
T & operator*() { assert( get() ); return *get(); }
const T & operator*() const { assert( get() ); return *get(); }
T * operator->() { return get(); }
const T * operator->() const { return get(); }
KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase )
};
template <typename T>
class MAKEINCLUDES_EXPORT KDLockedSharedMemoryArray : KDLockedSharedMemoryPointerBase {
KDAB_DISABLE_COPY( KDLockedSharedMemoryArray );
public:
explicit KDLockedSharedMemoryArray( QSharedMemory * m )
: KDLockedSharedMemoryPointerBase( m ) {}
explicit KDLockedSharedMemoryArray( QSharedMemory & m )
: KDLockedSharedMemoryPointerBase( m ) {}
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* iterator;
typedef const T* const_iterator;
typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
typedef std::reverse_iterator< iterator > reverse_iterator;
iterator begin() { return get(); }
const_iterator begin() const { return get(); }
iterator end() { return begin() + size(); }
const_iterator end() const { return begin() + size(); }
reverse_iterator rbegin() { return reverse_iterator( end() ); }
const_reverse_iterator rbegin() const { return reverse_iterator( end() ); }
reverse_iterator rend() { return reverse_iterator( begin() ); }
const_reverse_iterator rend() const { return const_reverse_iterator( begin() ); }
size_type size() const { return byteSize() / sizeof( T ); }
T * get() { return static_cast<T*>( KDLockedSharedMemoryPointerBase::get() ); }
const T * get() const { return static_cast<const T*>( KDLockedSharedMemoryPointerBase::get() ); }
T & operator[]( difference_type n ) { assert( get() ); return *(get()+n); }
const T & operator[]( difference_type n ) const { assert( get() ); return *(get()+n); }
T & front() { assert( get() ); return *get(); }
const T & front() const { assert( get() ); return *get(); }
T & back() { assert( get() ); return *( get() + size() - 1 ); }
const T & back() const { assert( get() ); return *( get() + size() - 1 ); }
KDAB_USING_SAFE_BOOL_OPERATOR( KDLockedSharedMemoryPointerBase )
};
#ifndef DOXYGEN_RUN
}
#endif
#endif /* QT_NO_SHAREDMEMORY */
#endif /* QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN ) */
#endif /* __KDTOOLS__CORE__KDLOCKEDSHAREDMEMORYPOINTER_H__ */

View File

@@ -0,0 +1,40 @@
#include "kdsharedmemorylocker.h"
#if QT_VERSION >= 0x040400 || defined( DOXYGEN_RUN )
#include <QSharedMemory>
using namespace kdtools;
/*!
\class KDSharedMemoryLocker
\ingroup raii core
\brief Exception-safe and convenient wrapper around QSharedMemory::lock()
*/
/**
* Constructor. Locks the shared memory segment \a mem.
* If another process has locking the segment, this constructor blocks
* until the lock is released. The memory segments needs to be properly created or attached.
*/
KDSharedMemoryLocker::KDSharedMemoryLocker( QSharedMemory* mem )
: mem( mem )
{
mem->lock();
}
/**
* Destructor. Unlocks the shared memory segment associated with this
* KDSharedMemoryLocker.
*/
KDSharedMemoryLocker::~KDSharedMemoryLocker()
{
mem->unlock();
}
#ifdef KDAB_EVAL
#include KDAB_EVAL
static const EvalDialogChecker evalChecker( "KD Tools", false );
#endif
#endif

View File

@@ -0,0 +1,36 @@
#ifndef __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H
#define __KDTOOLS__CORE__KDSHAREDMEMORYLOCKER_H
#include "kdtoolsglobal.h"
#if QT_VERSION < 0x040400 && !defined( DOXYGEN_RUN )
#ifdef Q_CC_GNU
#warning "Can't use KDTools KDSharedMemoryLocker with Qt versions prior to 4.4"
#endif
#else
class QSharedMemory;
#ifndef DOXYGEN_RUN
namespace kdtools
{
#endif
class KDTOOLSCORE_EXPORT KDSharedMemoryLocker
{
Q_DISABLE_COPY( KDSharedMemoryLocker )
public:
KDSharedMemoryLocker( QSharedMemory* mem );
~KDSharedMemoryLocker();
private:
QSharedMemory* const mem;
};
#ifndef DOXYGEN_RUN
}
#endif
#endif
#endif

View File

@@ -0,0 +1,622 @@
#include "kdsingleapplicationguard.h"
#ifndef KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES
#define KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES 128
#endif
#ifndef KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE
#define KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE 1024
#endif
KDSingleApplicationGuard::Instance::Instance( const QStringList& args, qint64 p )
: arguments( args ),
pid( p )
{
}
#if QT_VERSION < 0x040400
class KDSingleApplicationGuard::Private
{
};
KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication*, Policy )
{
qWarning( "KD Tools was compiled with a Qt version prior to 4.4. SingleApplicationGuard won't work." );
}
KDSingleApplicationGuard::~KDSingleApplicationGuard()
{
}
void KDSingleApplicationGuard::shutdownOtherInstances()
{
}
void KDSingleApplicationGuard::killOtherInstances()
{
}
void KDSingleApplicationGuard::timerEvent( QTimerEvent* )
{
}
#else
#include <QCoreApplication>
#include <QSharedMemory>
#include "kdsharedmemorylocker.h"
#include "kdlockedsharedmemorypointer.h"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#ifndef Q_WS_WIN
#include <csignal>
#endif
using namespace kdtools;
/*!
\class KDSingleApplicationGuard KDSingleApplicationGuard
\brief A guard to protect an application from having several instances.
KDSingleApplicationGuard can be used to make sure only one instance of an
application is running at the same time.
\note As KDSingleApplicationGuard uses QSharedMemory Qt 4.4 or later is required
*/
/*!
\fn void KDSingleApplicationGuard::instanceStarted()
This signal is emitted by the primary instance when ever one other
instance was started.
*/
/*!
\fn void KDSingleApplicationGuard::instanceExited()
This signal is emitted by the primary instance when ever one other
instance was exited.
*/
/*!
\fn void KDSingleApplicationGuard::becamePrimaryInstance()
This signal is emitted, when the current running application gets the new
primary application. The old primary application has quit.
*/
enum Command
{
NoCommand = 0x00,
ExitedInstance = 0x01,
NewInstance = 0x02,
FreeInstance = 0x04,
ShutDownCommand = 0x08,
KillCommand = 0x10,
BecomePrimaryCommand = 0x20
};
Q_DECLARE_FLAGS( Commands, Command )
Q_DECLARE_OPERATORS_FOR_FLAGS( Commands )
struct ProcessInfo
{
explicit ProcessInfo( Command c = FreeInstance, const QStringList& arguments = QStringList(), qint64 p = -1 )
: command( c ),
pid( p )
{
std::fill_n( commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE, '\0' );
int argpos = 0;
for( QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it )
{
const QByteArray arg = it->toLatin1();
const int count = qMin( KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE - argpos, arg.count() );
std::copy( arg.begin(), arg.begin() + count, commandline + argpos );
argpos += arg.count() + 1; // makes sure there's a \0 between every parameter
}
}
QStringList arguments() const
{
QStringList result;
QByteArray arg;
for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE; ++i )
{
if( commandline[ i ] == '\0' && !arg.isEmpty() )
{
result.push_back( QString::fromLatin1( arg ) );
arg.clear();
}
else if( !commandline[ i ] == '\0' )
{
arg.push_back( commandline[ i ] );
}
}
return result;
}
Commands command;
char commandline[ KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ];
qint64 pid;
};
bool operator==( const ProcessInfo& lhs, const ProcessInfo& rhs )
{
return lhs.command == rhs.command &&
::memcmp( lhs.commandline, rhs.commandline, KDSINGLEAPPLICATIONGUARD_MAX_COMMAND_LINE ) == 0;
}
bool operator!=( const ProcessInfo& lhs, const ProcessInfo& rhs )
{
return !operator==( lhs, rhs );
}
/*!
This struct contains information about the managed process system.
\internal
*/
struct InstanceRegister
{
InstanceRegister( KDSingleApplicationGuard::Policy policy = KDSingleApplicationGuard::NoPolicy )
: policy( policy )
{
std::fill_n( info, KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() );
::memcpy( magicCookie, "kdsingleapp", 12 );
}
/*!
Returns wheter this register was properly initialized by the first instance.
*/
bool isValid() const
{
return ::strcmp( magicCookie, "kdsingleapp" ) == 0;
}
ProcessInfo info[ KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES ];
KDSingleApplicationGuard::Policy policy;
char magicCookie[ 12 ];
};
bool operator==( const InstanceRegister& lhs, const InstanceRegister& rhs )
{
if( lhs.policy != rhs.policy )
return false;
for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i )
if( lhs.info[ i ] != rhs.info[ i ] )
return false;
return true;
}
/*!
\internal
*/
class KDSingleApplicationGuard::Private
{
public:
Private( KDSingleApplicationGuard* qq )
: q( qq ),
id( -1 )
{
if( primaryInstance == 0 )
primaryInstance = q;
}
~Private()
{
if( primaryInstance == q )
primaryInstance = 0;
}
void shutdownInstance()
{
KDLockedSharedMemoryPointer< InstanceRegister > instances( &q->d->mem );
instances->info[ q->d->id ].command = ExitedInstance;
if( q->isPrimaryInstance() )
{
// ohh... we need a new primary instance...
for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i )
{
if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance | ShutDownCommand | KillCommand ) ) == 0 )
{
instances->info[ i ].command |= BecomePrimaryCommand;
return;
}
}
// none found? then my species is dead :-(
}
}
static KDSingleApplicationGuard* primaryInstance;
private:
KDSingleApplicationGuard* const q;
public:
Policy policy;
QSharedMemory mem;
int id;
};
KDSingleApplicationGuard* KDSingleApplicationGuard::Private::primaryInstance = 0;
#ifndef Q_WS_WIN
void SIGINT_handler( int sig )
{
if( sig == SIGINT && KDSingleApplicationGuard::Private::primaryInstance != 0 )
KDSingleApplicationGuard::Private::primaryInstance->d->shutdownInstance();
::exit( 1 );
}
#endif
/*!
Creates a new KDSingleApplicationGuard guarding \a parent from mulitply instances.
If \a policy is AutoKillOtherInstances (the default), all instances, which try to start,
are killed automatically and instanceStarted() is emitted.
If \a policy is NoPolicy, the other instance will run and instanceStarted() is emitted.
*/
KDSingleApplicationGuard::KDSingleApplicationGuard( QCoreApplication* parent, Policy policy )
: QObject( parent ),
d( new Private( this ) )
{
const QString name = parent->applicationName();
Q_ASSERT_X( !name.isEmpty(), "KDSingleApplicationGuard::KDSingleApplicationGuard", "applicationName must not be emty" );
d->mem.setKey( name );
// if another instance crashed, the shared memory segment is still there on Unix
// the following lines trigger deletion in that case
#ifndef Q_WS_WIN
d->mem.attach();
d->mem.detach();
#endif
d->policy = policy;
const bool created = d->mem.create( sizeof( InstanceRegister ) );
if( !created )
{
if( !d->mem.attach() )
{
qWarning( "KDSingleApplicationGuard: Could neither create nor attach to shared memory segment." );
return;
}
// lets wait till the other instance initialized the register
bool initialized = false;
while( !initialized )
{
const KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
initialized = instances->isValid();
}
}
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
if( !created )
{
// we're _not_ the first instance
// but the
bool killOurSelf = false;
// find a new slot...
d->id = std::find( instances->info, instances->info + KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES, ProcessInfo() ) - instances->info;
ProcessInfo& info = instances->info[ d->id ];
info = ProcessInfo( NewInstance, parent->arguments(), QCoreApplication::applicationPid() );
killOurSelf = instances->policy == AutoKillOtherInstances;
d->policy = instances->policy;
// but the signal that we tried to start was sent to the primary application
if( killOurSelf )
{
info.command |= ExitedInstance;
exit( 1 );
}
}
else
{
// ok.... we are the first instance
InstanceRegister reg( policy ); // create a new list
d->id = 0; // our id = 0
// and we've no command
reg.info[ 0 ] = ProcessInfo( NoCommand, parent->arguments(), QCoreApplication::applicationPid() );
*instances = reg; // push this is the process list into shared memory
}
#ifndef Q_WS_WIN
::signal( SIGINT, SIGINT_handler );
#endif
// now listen for commands
startTimer( 250 );
}
/*!
Destroys this SingleApplicationGuard.
If this instance has been the primary instance and no other instance is existing anymore,
the application is shut down completely. Otherwise the destructor selects another instance to
be the primary instances.
*/
KDSingleApplicationGuard::~KDSingleApplicationGuard()
{
if( d->id == -1 )
return;
d->shutdownInstance();
}
/*!
\property KDSingleApplicationGuard::primaryInstance
Determines wheter this instance is the primary instance.
The primary instance is the first instance which was started or an instance which
got selected by KDSingleApplicationGuard's destructor, when the primary instance was
shut down.
Get this property's value using %isPrimaryInstance(), and monitor changes to it
using becamePrimaryInstance().
*/
bool KDSingleApplicationGuard::isPrimaryInstance() const
{
return d->id == 0;
}
/*!
\property KDSingleApplicationGuard::Policy
Specifies the policy KDSingleApplicationGuard is using when new instances are started.
This can only be set in the primary instance.
Get this property's value using %policy(), set it using %setPolicy(), and monitor changes
to it using policyChanged().
*/
KDSingleApplicationGuard::Policy KDSingleApplicationGuard::policy() const
{
return d->policy;
}
void KDSingleApplicationGuard::setPolicy( Policy policy )
{
Q_ASSERT( isPrimaryInstance() );
if( d->policy == policy )
return;
d->policy = policy;
emit policyChanged();
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
instances->policy = policy;
}
/*!
Returns a list of all currently running instances.
*/
QList< KDSingleApplicationGuard::Instance > KDSingleApplicationGuard::instances() const
{
QList< Instance > result;
const KDLockedSharedMemoryPointer< InstanceRegister > instances( const_cast< QSharedMemory* >( &d->mem ) );
for( int i = 0; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i )
{
const ProcessInfo& info = instances->info[ i ];
if( ( info.command & ( FreeInstance | ExitedInstance ) ) == 0 )
result.push_back( Instance( info.arguments(), info.pid ) );
}
return result;
}
/*!
Shuts down all other instances. This can only be called from the
the primary instance.
Shut down is done gracefully via QCoreApplication::quit().
*/
void KDSingleApplicationGuard::shutdownOtherInstances()
{
Q_ASSERT( isPrimaryInstance() );
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i )
{
if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
instances->info[ i ].command = ShutDownCommand;
}
}
/*!
Kills all other instances. This can only be called from the
the primary instance.
Killing is done via exit(1)
*/
void KDSingleApplicationGuard::killOtherInstances()
{
Q_ASSERT( isPrimaryInstance() );
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i )
{
if( ( instances->info[ i ].command & ( FreeInstance | ExitedInstance ) ) == 0 )
instances->info[ i ].command = KillCommand;
}
}
/*!
\reimp
*/
void KDSingleApplicationGuard::timerEvent( QTimerEvent* event )
{
Q_UNUSED( event )
if( isPrimaryInstance() )
{
// only the primary instance will get notified about new instances
QList< Instance > exitedInstances;
QList< Instance > startedInstances;
{
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
for( int i = 1; i < KDSINGLEAPPLICATIONGUARD_NUMBER_OF_PROCESSES; ++i )
{
ProcessInfo& info = instances->info[ i ];
if( info.command & NewInstance )
{
startedInstances.push_back( Instance( info.arguments(), info.pid ) );
info.command &= ~NewInstance; // clear NewInstance flag
}
else if( info.command & ExitedInstance )
{
exitedInstances.push_back( Instance( info.arguments(), info.pid ) );
info.command = FreeInstance; // set FreeInstance flag
}
}
}
// one signal for every new instance - _after_ the memory segment was unlocked again
for( QList< Instance >::const_iterator it = startedInstances.begin(); it != startedInstances.end(); ++it )
emit instanceStarted( *it );
for( QList< Instance >::const_iterator it = exitedInstances.begin(); it != exitedInstances.end(); ++it )
emit instanceExited( *it );
}
else
{
// do we have a command?
bool killOurSelf = false;
bool shutDownOurSelf = false;
bool policyDidChange = false;
{
KDLockedSharedMemoryPointer< InstanceRegister > instances( &d->mem );
policyDidChange = instances->policy != d->policy;
d->policy = instances->policy;
if( instances->info[ d->id ].command & BecomePrimaryCommand )
{
// we became primary!
instances->info[ 0 ] = instances->info[ d->id ];
instances->info[ d->id ] = ProcessInfo(); // change our id to 0 and declare the old slot as free
d->id = 0;
emit becamePrimaryInstance();
}
killOurSelf = instances->info[ d->id ].command & KillCommand; // check for kill command
shutDownOurSelf = instances->info[ d->id ].command & ShutDownCommand; // check for shut down command
instances->info[ d->id ].command &= ~( KillCommand | ShutDownCommand | BecomePrimaryCommand ); // reset both flags
if( killOurSelf )
{
instances->info[ d->id ].command |= ExitedInstance; // upon kill, we have to set the ExitedInstance flag
d->id = -1; // becauso our d'tor won't be called anymore
}
}
if( killOurSelf ) // kill our self takes precedence
exit( 1 );
else if( shutDownOurSelf )
qApp->quit();
else if( policyDidChange )
emit policyChanged();
}
}
#ifdef KDTOOLSCORE_UNITTESTS
#include <kdunittest/test.h>
#include <iostream>
#include <QtCore/QTime>
#include <QtCore/QUuid>
#include <QtTest/QSignalSpy>
Q_DECLARE_METATYPE( KDSingleApplicationGuard::Instance );
static void wait( int msec )
{
QTime t;
t.start();
while( t.elapsed() < msec )
{
qApp->processEvents( QEventLoop::WaitForMoreEvents, msec - t.elapsed() );
}
}
static std::ostream& operator<<( std::ostream& stream, const QStringList& list )
{
stream << "QStringList(";
for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it )
{
stream << " " << it->toLocal8Bit().data();
if( it + 1 != list.end() )
stream << ",";
}
stream << " )";
return stream;
}
KDAB_UNITTEST_SIMPLE( KDSingleApplicationGuard, "kdcoretools" ) {
// set it to an unique name
qApp->setApplicationName( QUuid::createUuid().toString() );
qRegisterMetaType< KDSingleApplicationGuard::Instance >();
KDSingleApplicationGuard* guard3 = 0;
QSignalSpy* spy3 = 0;
{
KDSingleApplicationGuard guard1( qApp );
assertEqual( guard1.policy(), KDSingleApplicationGuard::AutoKillOtherInstances );
assertEqual( guard1.instances().count(), 1 );
assertTrue( guard1.isPrimaryInstance() );
guard1.setPolicy( KDSingleApplicationGuard::NoPolicy );
assertEqual( guard1.policy(), KDSingleApplicationGuard::NoPolicy );
QSignalSpy spy1( &guard1, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) );
KDSingleApplicationGuard guard2( qApp );
assertEqual( guard1.instances().count(), 2 );
assertEqual( guard2.instances().count(), 2 );
assertEqual( guard2.policy(), KDSingleApplicationGuard::NoPolicy );
assertFalse( guard2.isPrimaryInstance() );
wait( 1000 );
assertEqual( spy1.count(), 1 );
guard3 = new KDSingleApplicationGuard( qApp );
spy3 = new QSignalSpy( guard3, SIGNAL( becamePrimaryInstance() ) );
assertFalse( guard3->isPrimaryInstance() );
}
wait( 1000 );
assertEqual( spy3->count(), 1 );
assertEqual( guard3->instances().count(), 1 );
assertTrue( guard3->isPrimaryInstance() );
assertEqual( guard3->instances().first().arguments, qApp->arguments() );
QSignalSpy spyStarted( guard3, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ) );
QSignalSpy spyExited( guard3, SIGNAL( instanceExited( KDSingleApplicationGuard::Instance ) ) );
{
KDSingleApplicationGuard guard1( qApp );
KDSingleApplicationGuard guard2( qApp );
wait( 1000 );
assertEqual( spyStarted.count(), 2 );
}
wait( 1000 );
assertEqual( spyExited.count(), 2 );
delete spy3;
delete guard3;
}
#endif // KDTOOLSCORE_UNITTESTS
#endif // QT_VERSION < 0x040400

View File

@@ -0,0 +1,75 @@
#ifndef __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__
#define __KDTOOLSCORE_KDSINGLEAPPLICATIONGUARD_H__
#include <QtCore/QObject>
#include <QtCore/QStringList>
#include "pimpl_ptr.h"
#include "dllmacro.h"
class QCoreApplication;
#ifndef Q_WS_WIN
void SIGINT_handler( int sig );
#endif
class DLLEXPORT KDSingleApplicationGuard : public QObject
{
Q_OBJECT
#ifndef Q_WS_WIN
friend void ::SIGINT_handler( int );
#endif
public:
enum Policy
{
NoPolicy = 0,
AutoKillOtherInstances = 1
};
Q_PROPERTY( bool primaryInstance READ isPrimaryInstance NOTIFY becamePrimaryInstance )
Q_PROPERTY( Policy policy READ policy WRITE setPolicy NOTIFY policyChanged )
explicit KDSingleApplicationGuard( QCoreApplication* parent, Policy policy = AutoKillOtherInstances );
~KDSingleApplicationGuard();
bool isPrimaryInstance() const;
Policy policy() const;
void setPolicy( Policy policy );
struct Instance
{
Instance( const QStringList& arguments = QStringList(), qint64 pid = -1 );
QStringList arguments;
qint64 pid;
};
QList< Instance > instances() const;
Q_SIGNALS:
void instanceStarted( KDSingleApplicationGuard::Instance instance );
void instanceExited( KDSingleApplicationGuard::Instance instance );
void becamePrimaryInstance();
void policyChanged();
public Q_SLOTS:
void shutdownOtherInstances();
void killOtherInstances();
protected:
void timerEvent( QTimerEvent* event );
private:
class Private;
kdtools::pimpl_ptr< Private > d;
};
#if QT_VERSION < 0x040400
#ifdef Q_CC_GNU
#warning "Can't use KDSingleApplicationGuard with Qt versions prior to 4.4"
#endif
#endif
#endif

View File

@@ -0,0 +1,32 @@
#include "kdtoolsglobal.h"
#include <QByteArray>
#include <algorithm>
namespace {
struct Version {
unsigned char v[3];
};
static inline bool operator<( const Version & lhs, const Version & rhs ) {
return std::lexicographical_compare( lhs.v, lhs.v + 3, rhs.v, rhs.v + 3 );
}
static inline bool operator==( const Version & lhs, const Version & rhs ) {
return std::equal( lhs.v, lhs.v + 3, rhs.v );
}
KDTOOLS_MAKE_RELATION_OPERATORS( Version, static inline )
}
static Version kdParseQtVersion( const char * const version ) {
if ( !version || qstrlen( version ) < 5 || version[1] != '.' || version[3] != '.' || version[5] != 0 && version[5] != '.' && version[5] != '-' )
return Version(); // parse error
const Version result = { { version[0] - '0', version[2] - '0', version[4] - '0' } };
return result;
}
bool _kdCheckQtVersion_impl( int major, int minor, int patchlevel ) {
static const Version actual = kdParseQtVersion( qVersion() ); // do this only once each run...
const Version requested = { { major, minor, patchlevel } };
return actual >= requested;
}

View File

@@ -0,0 +1,113 @@
#ifndef __KDTOOLS_KDTOOLSGLOBAL_H__
#define __KDTOOLS_KDTOOLSGLOBAL_H__
#include <QtCore/QtGlobal>
#define KDAB_DISABLE_COPY( x ) private: x( const x & ); x & operator=( const x & )
#ifdef KDTOOLS_SHARED
# ifdef BUILD_SHARED_KDTOOLSCORE
# define KDTOOLSCORE_EXPORT Q_DECL_EXPORT
# else
# define KDTOOLSCORE_EXPORT Q_DECL_IMPORT
# endif
# ifdef BUILD_SHARED_KDTOOLSGUI
# define KDTOOLSGUI_EXPORT Q_DECL_EXPORT
# else
# define KDTOOLSGUI_EXPORT Q_DECL_IMPORT
# endif
# ifdef BUILD_SHARED_KDTOOLSXML
# define KDTOOLSXML_EXPORT Q_DECL_EXPORT
# else
# define KDTOOLSXML_EXPORT Q_DECL_IMPORT
# endif
# ifdef BUILD_SHARED_KDUPDATER
# define KDTOOLS_UPDATER_EXPORT Q_DECL_EXPORT
# else
# define KDTOOLS_UPDATER_EXPORT Q_DECL_IMPORT
# endif
#else // KDTOOLS_SHARED
# define KDTOOLSCORE_EXPORT
# define KDTOOLSGUI_EXPORT
# define KDTOOLSXML_EXPORT
# define KDTOOLS_UPDATER_EXPORT
#endif // KDTOOLS_SHARED
#define MAKEINCLUDES_EXPORT
#define DOXYGEN_PROPERTY( x )
#ifdef DOXYGEN_RUN
# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) operator unspecified_bool_type() const { return func; }
# define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) operator unspecified_bool_type() const;
#else
# define KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( func ) \
private: struct __safe_bool_dummy__ { void nonnull() {} }; \
typedef void ( __safe_bool_dummy__::*unspecified_bool_type )(); \
public: \
operator unspecified_bool_type() const { \
return ( func ) ? &__safe_bool_dummy__::nonnull : 0 ; \
}
#define KDAB_USING_SAFE_BOOL_OPERATOR( Class ) \
using Class::operator Class::unspecified_bool_type;
#endif
#define KDTOOLS_MAKE_RELATION_OPERATORS( Class, linkage ) \
linkage bool operator>( const Class & lhs, const Class & rhs ) { \
return operator<( rhs, lhs ); \
} \
linkage bool operator!=( const Class & lhs, const Class & rhs ) { \
return !operator==( lhs, rhs ); \
} \
linkage bool operator<=( const Class & lhs, const Class & rhs ) { \
return !operator>( lhs, rhs ); \
} \
linkage bool operator>=( const Class & lhs, const Class & rhs ) { \
return !operator<( lhs, rhs ); \
}
template <typename T>
inline T & __kdtools__dereference_for_methodcall( T & o ) {
return o;
}
template <typename T>
inline T & __kdtools__dereference_for_methodcall( T * o ) {
return *o;
}
#define KDAB_SET_OBJECT_NAME( x ) __kdtools__dereference_for_methodcall( x ).setObjectName( QLatin1String( #x ) )
KDTOOLSCORE_EXPORT bool _kdCheckQtVersion_impl( int major, int minor=0, int patchlevel=0 );
static inline bool kdCheckQtVersion( unsigned int major, unsigned int minor=0, unsigned int patchlevel=0 ) {
return (major<<16|minor<<8|patchlevel) <= static_cast<unsigned int>(QT_VERSION)
|| _kdCheckQtVersion_impl( major, minor, patchlevel );
}
#define KDTOOLS_DECLARE_PRIVATE_BASE( Class ) \
protected: \
class Private; \
Private * d_func() { return _d; } \
const Private * d_func() const { return _d; } \
Class( Private * _d_, bool b ) : _d( _d_ ) { init(b); } \
private: \
void init(bool); \
private: \
Private * _d
#define KDTOOLS_DECLARE_PRIVATE_DERIVED( Class, Base ) \
protected: \
class Private; \
Private * d_func() { \
return reinterpret_cast<Private*>( Base::d_func() ); \
} \
const Private * d_func() const { \
return reinterpret_cast<const Private*>( Base::d_func() ); \
} \
Class( Private * _d_, bool b ) \
: Base( reinterpret_cast<Base::Private*>(_d_), b ) { init(b); } \
private: \
void init(bool)
#endif /* __KDTOOLS_KDTOOLSGLOBAL_H__ */

View File

@@ -0,0 +1,349 @@
The KD Tools Library is Copyright (C) 2001-2003 Klar<61>lvdalens Datakonsult AB.
You may use, distribute and copy the KD Tools Library under the terms of
GNU General Public License version 2, which is displayed below.
-------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
-------------------------------------------------------------------------

View File

@@ -0,0 +1,203 @@
#include "pimpl_ptr.h"
/*!
\class pimpl_ptr:
\ingroup core smartptr
\brief Owning pointer for private implementations
\since_c 2.1
(The exception safety of this class has not been evaluated yet.)
pimpl_ptr is a smart immutable pointer, which owns the contained object. Unlike other smart pointers,
it creates a standard constructed object when instanciated via the
\link pimpl_ptr() standard constructor\endlink.
Additionally, pimpl_ptr respects constness of the pointer object and returns \c const \c T* for
a const pimpl_ptr object.
The content of a pimpl_ptr cannot be changed during it's lifetime.
\section general-use General Use
The general use case of pimpl_ptr is the "Pimpl Idiom", i.e. hiding the private implementation of a class
from the user's compiler which see \c MyClass as
\code
class MyClass
{
public:
MyClass();
~MyClass();
// public class API
int value() const;
private:
class Private; // defined later
kdtools::pimpl_ptr< Private > d;
};
\endcode
but not the private parts of it. These can only be seen (and accessed) by the code knowing \c MyClass::Private:
\code
class MyClass::Private
{
public:
int value;
};
MyClass::MyClass()
{
// d was automatically filled with new Private
d->value = 42;
}
MyClass::~MyClass()
{
// the content of d gets deleted automatically
}
int MyClass::value() const
{
// access the private part:
// since MyClass::value() is const, the returned pointee is const, too
return d->value;
}
\endcode
*/
/*!
\fn pimpl_ptr::pimpl_ptr()
Default constructor. Constructs a pimpl_tr that contains (owns) a standard constructed
instance of \c T.
\post \c *this owns a new object.
*/
/*!
\fn pimpl_ptr::pimpl_ptr( T * t )
Constructor. Constructs a pimpl_ptr that contains (owns) \a t.
\post get() == obj
*/
/*!
\fn pimpl_ptr::~pimpl_ptr()
Destructor.
\post The object previously owned by \c *this has been deleted.
*/
/*!
\fn const T * pimpl_ptr::get() const
\returns a const pointer to the contained (owned) object.
\overload
*/
/*!
\fn T * pimpl_ptr::get()
\returns a pointer to the contained (owned) object.
*/
/*!
\fn const T & pimpl_ptr::operator*() const
Dereference operator. Returns \link get() *get()\endlink.
\overload
*/
/*!
\fn T & pimpl_ptr::operator*()
Dereference operator. Returns \link get() *get()\endlink.
*/
/*!
\fn const T * pimpl_ptr::operator->() const
Member-by-pointer operator. Returns get().
\overload
*/
/*!
\fn T * pimpl_ptr::operator->()
Member-by-pointer operator. Returns get().
*/
#ifdef KDTOOLSCORE_UNITTESTS
#include <kdunittest/test.h>
#include <QObject>
#include <QPointer>
namespace
{
struct ConstTester
{
bool isConst()
{
return false;
}
bool isConst() const
{
return true;
}
};
}
KDAB_UNITTEST_SIMPLE( pimpl_ptr, "kdcoretools" ) {
{
kdtools::pimpl_ptr< QObject > p;
assertNotNull( p.get() );
assertNull( p->parent() );
}
{
QPointer< QObject > o;
{
kdtools::pimpl_ptr< QObject > qobject( new QObject );
o = qobject.get();
assertEqual( o, qobject.operator->() );
assertEqual( o, &(qobject.operator*()) );
}
assertNull( o );
}
{
const kdtools::pimpl_ptr< QObject > qobject( new QObject );
const QObject* o = qobject.get();
assertEqual( o, qobject.operator->() );
assertEqual( o, &(qobject.operator*()) );
}
{
kdtools::pimpl_ptr< QObject > o1;
assertTrue( o1 );
kdtools::pimpl_ptr< QObject > o2( 0 );
assertFalse( o2 );
}
{
const kdtools::pimpl_ptr< ConstTester > o1;
kdtools::pimpl_ptr< ConstTester > o2;
assertTrue( o1->isConst() );
assertFalse( o2->isConst() );
assertTrue( (*o1).isConst() );
assertFalse( (*o2).isConst() );
assertTrue( o1.get()->isConst() );
assertFalse( o2.get()->isConst() );
}
}
#endif // KDTOOLSCORE_UNITTESTS

View File

@@ -0,0 +1,44 @@
#ifndef __KDTOOLSCORE__PIMPL_PTR_H__
#define __KDTOOLSCORE__PIMPL_PTR_H__
#include "kdtoolsglobal.h"
#ifndef DOXYGEN_RUN
namespace kdtools {
#endif
template <typename T>
class pimpl_ptr {
KDAB_DISABLE_COPY( pimpl_ptr );
T * d;
public:
pimpl_ptr() : d( new T ) {}
explicit pimpl_ptr( T * t ) : d( t ) {}
~pimpl_ptr() { delete d; d = 0; }
T * get() { return d; }
const T * get() const { return d; }
T * operator->() { return get(); }
const T * operator->() const { return get(); }
T & operator*() { return *get(); }
const T & operator*() const { return *get(); }
KDAB_IMPLEMENT_SAFE_BOOL_OPERATOR( get() )
};
// these are not implemented, so's we can catch their use at
// link-time. Leaving them undeclared would open up a comparison
// via operator unspecified-bool-type().
template <typename T, typename S>
void operator==( const pimpl_ptr<T> &, const pimpl_ptr<S> & );
template <typename T, typename S>
void operator!=( const pimpl_ptr<T> &, const pimpl_ptr<S> & );
#ifndef DOXYGEN_RUN
} // namespace kdtools
#endif
#endif /* __KDTOOLSCORE__PIMPL_PTR_H__ */

View File

@@ -62,16 +62,12 @@ Connection::Connection( Servent* parent )
Connection::~Connection()
{
qDebug() << "DTOR connection (super)" << id() << thread();
qDebug() << "DTOR connection (super)" << id() << thread() << m_sock.isNull();
if( !m_sock.isNull() )
{
qDebug() << "deleteLatering sock" << m_sock;
// qDebug() << "deleteLatering sock" << m_sock;
m_sock->deleteLater();
}
else
{
qDebug() << "no valid sock to delete";
}
delete m_statstimer;
}
@@ -118,7 +114,7 @@ Connection::setFirstMessage( msg_ptr m )
void
Connection::shutdown( bool waitUntilSentAll )
{
qDebug() << Q_FUNC_INFO << waitUntilSentAll;
qDebug() << Q_FUNC_INFO << waitUntilSentAll << id();
if ( m_do_shutdown )
{
//qDebug() << id() << " already shutting down";
@@ -128,7 +124,7 @@ Connection::shutdown( bool waitUntilSentAll )
m_do_shutdown = true;
if ( !waitUntilSentAll )
{
qDebug() << "Shutting down immediately " << id();
// qDebug() << "Shutting down immediately " << id();
actualShutdown();
}
else
@@ -146,10 +142,9 @@ Connection::shutdown( bool waitUntilSentAll )
void
Connection::actualShutdown()
{
qDebug() << Q_FUNC_INFO;
qDebug() << Q_FUNC_INFO << m_actually_shutting_down << id();
if( m_actually_shutting_down )
{
qDebug() << "(already actually shutting down)";
return;
}
m_actually_shutting_down = true;
@@ -159,7 +154,7 @@ Connection::actualShutdown()
m_sock->disconnectFromHost();
}
qDebug() << "EMITTING finished()";
// qDebug() << "EMITTING finished()";
emit finished();
}

View File

@@ -75,6 +75,13 @@ ControlConnection::setup()
{
qDebug() << Q_FUNC_INFO << id() << name();
if ( !m_source.isNull() )
{
qDebug() << "This source seems to be online already.";
Q_ASSERT( false );
return;
}
QString friendlyName;
if ( Servent::isIPWhitelisted( m_sock->peerAddress() ) )
{

View File

@@ -75,7 +75,7 @@ DBSyncConnection::~DBSyncConnection()
void
DBSyncConnection::idleTimeout()
{
qDebug() << Q_FUNC_INFO << "*************";
qDebug() << Q_FUNC_INFO;
shutdown( true );
}
@@ -87,11 +87,6 @@ DBSyncConnection::changeState( State newstate )
m_state = newstate;
qDebug() << "DBSYNC State changed from" << s << "to" << newstate;
emit stateChanged( newstate, s, "" );
if ( newstate == SYNCED )
{
qDebug() << "Synced :)";
}
}
@@ -197,7 +192,7 @@ DBSyncConnection::handleMsg( msg_ptr msg )
msg->is( Msg::DBOP ) &&
msg->payload() == "ok" )
{
qDebug() << "No ops to apply, we are synced.";
// qDebug() << "No ops to apply, we are synced.";
changeState( SYNCED );
// calc the collection stats, to updates the "X tracks" in the sidebar etc
// this is done automatically if you run a dbcmd to add files.

View File

@@ -676,12 +676,12 @@ Servent::checkACL( const Connection* conn, const QString &nodeid, bool showDialo
qDebug() << "ACL for this node not found";
QMessageBox msgBox;
msgBox.setIcon( QMessageBox::Question );
msgBox.setText( "Incoming Connection Attempt" );
msgBox.setInformativeText( QString( "Another Tomahawk instance is attempting to connect to you. Select whether to allow or deny this connection.\n\nPeer name: %1\nPeer ID: %2\n\nRemember: Only allow peers to connect if you have the legal right for them to stream music from you.").arg( conn->name(), nodeid ) );
QPushButton *denyButton = msgBox.addButton( "Deny", QMessageBox::HelpRole );
QPushButton *alwaysDenyButton = msgBox.addButton( "Always Deny", QMessageBox::YesRole );
QPushButton *allowButton = msgBox.addButton( "Allow", QMessageBox::NoRole );
QPushButton *alwaysAllowButton = msgBox.addButton( "Always Allow", QMessageBox::ActionRole );
msgBox.setText( tr( "Incoming Connection Attempt" ) );
msgBox.setInformativeText( tr( "Another Tomahawk instance is attempting to connect to you. Select whether to allow or deny this connection.\n\nPeer name: %1\nPeer ID: %2\n\nRemember: Only allow peers to connect if you have the legal right for them to stream music from you.").arg( conn->name(), nodeid ) );
QPushButton *denyButton = msgBox.addButton( tr( "Deny" ), QMessageBox::HelpRole );
QPushButton *alwaysDenyButton = msgBox.addButton( tr( "Always Deny" ), QMessageBox::YesRole );
QPushButton *allowButton = msgBox.addButton( tr( "Allow" ), QMessageBox::NoRole );
QPushButton *alwaysAllowButton = msgBox.addButton( tr( "Always Allow" ), QMessageBox::ActionRole );
msgBox.setDefaultButton( denyButton );
msgBox.setEscapeButton( denyButton );
@@ -792,8 +792,11 @@ Servent::isIPWhitelisted( QHostAddress ip )
bool
Servent::connectedToSession( const QString& session )
{
qDebug() << Q_FUNC_INFO;
qDebug() << "Checking against " << session;
foreach( ControlConnection* cc, m_controlconnections )
{
qDebug() << "Checking session " << cc->id();
if( cc->id() == session )
return true;
}

View File

@@ -102,7 +102,7 @@ Pipeline::resolve( const QList<query_ptr>& qlist, bool prioritized )
int i = 0;
foreach( const query_ptr& q, qlist )
{
qDebug() << Q_FUNC_INFO << (qlonglong)q.data() << q->toString();
// qDebug() << Q_FUNC_INFO << (qlonglong)q.data() << q->toString();
if ( !m_qids.contains( q->id() ) )
{
m_qids.insert( q->id(), q );
@@ -189,7 +189,7 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results )
if ( decQIDState( q ) == 0 )
{
// All resolvers have reported back their results for this query now
qDebug() << "Finished resolving:" << q->toString();
qDebug() << "Finished resolving:" << q->toString() << q->numResults();
if ( !q->solved() )
q->onResolvingFinished();
@@ -216,7 +216,7 @@ Pipeline::shuntNext()
return;
}
qDebug() << Q_FUNC_INFO << m_qidsState.count();
// qDebug() << Q_FUNC_INFO << m_qidsState.count();
// Check if we are ready to dispatch more queries
if ( m_qidsState.count() >= CONCURRENT_QUERIES )
return;
@@ -246,8 +246,8 @@ Pipeline::shunt( const query_ptr& q )
if ( q->solved() )
{
qDebug() << "Query solved, pipeline aborted:" << q->toString()
<< "numresults:" << q->results().length();
// qDebug() << "Query solved, pipeline aborted:" << q->toString()
// << "numresults:" << q->results().length();
QList< result_ptr > rl;
reportResults( q->id(), rl );
@@ -275,7 +275,7 @@ Pipeline::shunt( const query_ptr& q )
lasttimeout = r->timeout();
// resolvers aren't allowed to block in this call:
qDebug() << "Dispatching to resolver" << r->name();
qDebug() << "Dispatching to resolver" << r->name() << q->toString();
thisResolver = i;
r->resolve( q );
@@ -291,7 +291,7 @@ Pipeline::shunt( const query_ptr& q )
if ( thisResolver < m_resolvers.count() )
{
incQIDState( q );
qDebug() << "Shunting in" << lasttimeout << "ms, q:" << q->toString();
// qDebug() << "Shunting in" << lasttimeout << "ms, q:" << q->toString();
new FuncTimeout( lasttimeout, boost::bind( &Pipeline::shunt, this, q ) );
}
}
@@ -329,7 +329,7 @@ Pipeline::incQIDState( const Tomahawk::query_ptr& query )
state = m_qidsState.value( query->id() ) + 1;
}
qDebug() << Q_FUNC_INFO << "inserting to qidsstate:" << query->id() << state;
// qDebug() << Q_FUNC_INFO << "inserting to qidsstate:" << query->id() << state;
m_qidsState.insert( query->id(), state );
return state;
@@ -344,12 +344,12 @@ Pipeline::decQIDState( const Tomahawk::query_ptr& query )
int state = m_qidsState.value( query->id() ) - 1;
if ( state )
{
qDebug() << Q_FUNC_INFO << "replacing" << query->id() << state;
// qDebug() << Q_FUNC_INFO << "replacing" << query->id() << state;
m_qidsState.insert( query->id(), state );
}
else
{
qDebug() << Q_FUNC_INFO << "removing" << query->id() << state;
// qDebug() << Q_FUNC_INFO << "removing" << query->id() << state;
m_qidsState.remove( query->id() );
}

View File

@@ -112,7 +112,7 @@ Playlist::Playlist( const source_ptr& src,
, m_lastmodified( lastmod )
, m_shared( shared )
{
qDebug() << Q_FUNC_INFO << "1";
// qDebug() << Q_FUNC_INFO << "1";
init();
}

View File

@@ -253,8 +253,8 @@ DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self )
Q_ASSERT( !author().isNull() );
Q_ASSERT( !author()->collection().isNull() );
// will emit Collection::playlistCreated(...)
qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull();
qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName();
// qDebug() << "Creating dynplaylist belonging to:" << author().data() << author().isNull();
// qDebug() << "REPORTING DYNAMIC PLAYLIST CREATED:" << this << author()->friendlyName();
author()->collection()->addDynamicPlaylist( self );
}
@@ -431,7 +431,7 @@ QList< dyncontrol_ptr > DynamicPlaylist::variantsToControl( const QList< QVarian
QList<dyncontrol_ptr> realControls;
foreach( QVariantMap controlV, controlsV ) {
dyncontrol_ptr control = GeneratorFactory::createControl( controlV.value( "type" ).toString(), controlV.value( "selectedType" ).toString() );
qDebug() << "CReating control with data:" << controlV;
qDebug() << "Creating control with data:" << controlV;
QJson::QObjectHelper::qvariant2qobject( controlV, control.data() );
realControls << control;
}

View File

@@ -345,6 +345,8 @@ DynamicWidget::controlsChanged()
return;
m_playlist->createNewRevision();
m_seqRevLaunched++;
emit descriptionChanged( m_playlist->generator()->sentenceSummary() );
}
void
@@ -356,6 +358,8 @@ DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control )
m_seqRevLaunched++;
showPreview();
emit descriptionChanged( m_playlist->generator()->sentenceSummary() );
}
void

View File

@@ -94,6 +94,9 @@ public slots:
void playlistChanged( PlaylistInterface* );
void tracksAdded();
signals:
void descriptionChanged( const QString& caption );
private slots:
void generate( int = -1 );
void tracksGenerated( const QList< Tomahawk::query_ptr>& queries );

View File

@@ -72,14 +72,21 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti
if ( !item || item->query().isNull() )
return;
float opacity = 0.0;
painter->save();
if ( item->query()->results().count() )
painter->setOpacity( item->query()->results().at( 0 )->score() );
else
painter->setOpacity( 0.0 );
opacity = item->query()->results().first()->score();
if ( painter->opacity() < 0.3 )
painter->setOpacity( 0.3 );
QColor textcol, bgcol;
textcol = option.palette.color( QPalette::Foreground );
bgcol = option.palette.color( QPalette::Background );
opacity = qMax( (float)0.3, opacity );
int r = textcol.red(), g = textcol.green(), b = textcol.blue();
r = opacity * r + ( 1 - opacity ) * bgcol.red();
g = opacity * g + ( 1 - opacity ) * bgcol.green();
b = opacity * b + ( 1 - opacity ) * bgcol.blue();
textcol = QColor( r, g, b );
if ( item->isPlaying() )
{
@@ -113,6 +120,13 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti
}
else
{
if ( const QStyleOptionViewItem *vioption = qstyleoption_cast<const QStyleOptionViewItem *>(&option))
{
QStyleOptionViewItemV4 o( *vioption );
o.palette.setColor( QPalette::Text, textcol );
QStyledItemDelegate::paint( painter, o, index );
}
else
QStyledItemDelegate::paint( painter, option, index );
}

View File

@@ -553,6 +553,13 @@ PlaylistManager::setPage( ViewPage* page, bool trackHistory )
if ( !AudioEngine::instance()->isPlaying() )
AudioEngine::instance()->setPlaylist( currentPlaylistInterface() );
// UGH!
if( QObject* obj = dynamic_cast< QObject* >( currentPage() ) ) {
// qDebug() << SIGNAL( descriptionChanged( QString ) ) << QMetaObject::normalizedSignature( SIGNAL( descriptionChanged( QString ) ) ) << obj->metaObject()->indexOfSignal( QMetaObject::normalizedSignature( SIGNAL( descriptionChanged( QString ) ) ) );
// if( obj->metaObject()->indexOfSignal( QMetaObject::normalizedSignature( SIGNAL( descriptionChanged( QString ) ) ) ) > -1 ) // if the signal exists (just to hide the qobject runtime warning...)
connect( obj, SIGNAL( descriptionChanged( QString ) ), m_infobar, SLOT( setDescription( QString ) ) );
}
m_stack->setCurrentWidget( page->widget() );
updateView();
}

View File

@@ -396,12 +396,16 @@ PlaylistModel::playlistEntries() const
}
void
PlaylistModel::remove( unsigned int row, bool moreToCome )
{
removeIndex( index( row, 0, QModelIndex() ), moreToCome );
}
void
PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome )
{
if ( isReadOnly() )
return;
TrackModel::removeIndex( index );
if ( !moreToCome && !m_playlist.isNull() )

View File

@@ -62,6 +62,7 @@ public:
void insert( unsigned int row, const Tomahawk::query_ptr& query );
void remove( unsigned int row, bool moreToCome = false );
virtual void removeIndex( const QModelIndex& index, bool moreToCome = false );
signals:

View File

@@ -26,6 +26,7 @@
#include "utils/tomahawkutils.h"
#include "album.h"
#include "pipeline.h"
using namespace Tomahawk;
@@ -302,7 +303,7 @@ TrackModel::removeIndex( const QModelIndex& index, bool moreToCome )
{
if ( QThread::currentThread() != thread() )
{
qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO;
// qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO;
QMetaObject::invokeMethod( this, "removeIndex",
Qt::QueuedConnection,
Q_ARG(const QModelIndex, index),
@@ -311,8 +312,6 @@ TrackModel::removeIndex( const QModelIndex& index, bool moreToCome )
return;
}
qDebug() << Q_FUNC_INFO;
if ( index.column() > 0 )
return;
@@ -370,3 +369,16 @@ TrackModel::onPlaybackStopped()
oldEntry->setIsPlaying( false );
}
}
void
TrackModel::ensureResolved()
{
for( int i = 0; i < rowCount( QModelIndex() ); i++ )
{
query_ptr query = itemFromIndex( index( i, 0, QModelIndex() ) )->query();
if ( !query->numResults() )
Pipeline::instance()->resolve( query );
}
}

View File

@@ -76,6 +76,8 @@ public:
virtual PlaylistInterface::RepeatMode repeatMode() const { return PlaylistInterface::NoRepeat; }
virtual bool shuffled() const { return false; }
virtual void ensureResolved();
virtual void append( const Tomahawk::query_ptr& query ) = 0;
PlItem* itemFromIndex( const QModelIndex& index ) const;

View File

@@ -1,199 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/
#include "qtlocalpeer.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QTime>
#if defined(Q_OS_WIN)
#include <QtCore/QLibrary>
#include <QtCore/qt_windows.h>
typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
static PProcessIdToSessionId pProcessIdToSessionId = 0;
#endif
#if defined(Q_OS_UNIX)
#include <time.h>
#endif
namespace QtLP_Private {
#include "qtlockedfile.cpp"
#if defined(Q_OS_WIN)
#include "qtlockedfile_win.cpp"
#else
#include "qtlockedfile_unix.cpp"
#endif
}
const char* QtLocalPeer::ack = "ack";
QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
: QObject(parent), id(appId)
{
QString prefix = id;
if (id.isEmpty()) {
id = QCoreApplication::applicationFilePath();
#if defined(Q_OS_WIN)
id = id.toLower();
#endif
prefix = id.section(QLatin1Char('/'), -1);
}
prefix.remove(QRegExp("[^a-zA-Z]"));
prefix.truncate(6);
QByteArray idc = id.toUtf8();
quint16 idNum = qChecksum(idc.constData(), idc.size());
socketName = QLatin1String("qtsingleapp-") + prefix
+ QLatin1Char('-') + QString::number(idNum, 16);
#if defined(Q_OS_WIN)
if (!pProcessIdToSessionId) {
QLibrary lib("kernel32");
pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
}
if (pProcessIdToSessionId) {
DWORD sessionId = 0;
pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
socketName += QLatin1Char('-') + QString::number(sessionId, 16);
}
#else
socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
#endif
server = new QLocalServer(this);
QString lockName = QDir(QDir::tempPath()).absolutePath()
+ QLatin1Char('/') + socketName
+ QLatin1String("-lockfile");
lockFile.setFileName(lockName);
lockFile.open(QIODevice::ReadWrite);
}
bool QtLocalPeer::isClient()
{
if (lockFile.isLocked())
return false;
if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false))
return true;
bool res = server->listen(socketName);
#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0))
// ### Workaround
if (!res && server->serverError() == QAbstractSocket::AddressInUseError) {
QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName);
res = server->listen(socketName);
}
#endif
if (!res)
qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection()));
return false;
}
bool QtLocalPeer::sendMessage(const QString &message, int timeout)
{
if (!isClient())
return false;
QLocalSocket socket;
bool connOk = false;
for(int i = 0; i < 2; i++) {
// Try twice, in case the other instance is just starting up
socket.connectToServer(socketName);
connOk = socket.waitForConnected(timeout/2);
if (connOk || i)
break;
int ms = 250;
#if defined(Q_OS_WIN)
Sleep(DWORD(ms));
#else
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep(&ts, NULL);
#endif
}
if (!connOk)
return false;
QByteArray uMsg(message.toUtf8());
QDataStream ds(&socket);
ds.writeBytes(uMsg.constData(), uMsg.size());
bool res = socket.waitForBytesWritten(timeout);
if (res) {
res &= socket.waitForReadyRead(timeout); // wait for ack
if (res)
res &= (socket.read(qstrlen(ack)) == ack);
}
return res;
}
void QtLocalPeer::receiveConnection()
{
QLocalSocket* socket = server->nextPendingConnection();
if (!socket)
return;
while (socket->bytesAvailable() < (int)sizeof(quint32))
socket->waitForReadyRead();
QDataStream ds(socket);
QByteArray uMsg;
quint32 remaining;
ds >> remaining;
uMsg.resize(remaining);
int got = 0;
char* uMsgBuf = uMsg.data();
do {
got = ds.readRawData(uMsgBuf, remaining);
remaining -= got;
uMsgBuf += got;
} while (remaining && got >= 0 && socket->waitForReadyRead(2000));
if (got < 0) {
qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData());
delete socket;
return;
}
QString message(QString::fromUtf8(uMsg));
socket->write(ack, qstrlen(ack));
socket->waitForBytesWritten(1000);
delete socket;
emit messageReceived(message); //### (might take a long time to return)
}

View File

@@ -1,72 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include <QtCore/QDir>
#include "qtlockedfile.h"
class QtLocalPeer : public QObject
{
Q_OBJECT
public:
QtLocalPeer(QObject *parent = 0, const QString &appId = QString());
bool isClient();
bool sendMessage(const QString &message, int timeout);
QString applicationId() const
{ return id; }
Q_SIGNALS:
void messageReceived(const QString &message);
protected Q_SLOTS:
void receiveConnection();
protected:
QString id;
QString socketName;
QLocalServer* server;
QtLP_Private::QtLockedFile lockFile;
private:
static const char* ack;
};

View File

@@ -1,192 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/
#include "qtlockedfile.h"
/*!
\class QtLockedFile
\brief The QtLockedFile class extends QFile with advisory locking
functions.
A file may be locked in read or write mode. Multiple instances of
\e QtLockedFile, created in multiple processes running on the same
machine, may have a file locked in read mode. Exactly one instance
may have it locked in write mode. A read and a write lock cannot
exist simultaneously on the same file.
The file locks are advisory. This means that nothing prevents
another process from manipulating a locked file using QFile or
file system functions offered by the OS. Serialization is only
guaranteed if all processes that access the file use
QLockedFile. Also, while holding a lock on a file, a process
must not open the same file again (through any API), or locks
can be unexpectedly lost.
The lock provided by an instance of \e QtLockedFile is released
whenever the program terminates. This is true even when the
program crashes and no destructors are called.
*/
/*! \enum QtLockedFile::LockMode
This enum describes the available lock modes.
\value ReadLock A read lock.
\value WriteLock A write lock.
\value NoLock Neither a read lock nor a write lock.
*/
/*!
Constructs an unlocked \e QtLockedFile object. This constructor
behaves in the same way as \e QFile::QFile().
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile()
: QFile()
{
#ifdef Q_OS_WIN
wmutex = 0;
rmutex = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Constructs an unlocked QtLockedFile object with file \a name. This
constructor behaves in the same way as \e QFile::QFile(const
QString&).
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile(const QString &name)
: QFile(name)
{
#ifdef Q_OS_WIN
wmutex = 0;
rmutex = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Opens the file in OpenMode \a mode.
This is identical to QFile::open(), with the one exception that the
Truncate mode flag is disallowed. Truncation would conflict with the
advisory file locking, since the file would be modified before the
write lock is obtained. If truncation is required, use resize(0)
after obtaining the write lock.
Returns true if successful; otherwise false.
\sa QFile::open(), QFile::resize()
*/
bool QtLockedFile::open(OpenMode mode)
{
if (mode & QIODevice::Truncate) {
qWarning("QtLockedFile::open(): Truncate mode not allowed.");
return false;
}
return QFile::open(mode);
}
/*!
Returns \e true if this object has a in read or write lock;
otherwise returns \e false.
\sa lockMode()
*/
bool QtLockedFile::isLocked() const
{
return m_lock_mode != NoLock;
}
/*!
Returns the type of lock currently held by this object, or \e
QtLockedFile::NoLock.
\sa isLocked()
*/
QtLockedFile::LockMode QtLockedFile::lockMode() const
{
return m_lock_mode;
}
/*!
\fn bool QtLockedFile::lock(LockMode mode, bool block = true)
Obtains a lock of type \a mode. The file must be opened before it
can be locked.
If \a block is true, this function will block until the lock is
aquired. If \a block is false, this function returns \e false
immediately if the lock cannot be aquired.
If this object already has a lock of type \a mode, this function
returns \e true immediately. If this object has a lock of a
different type than \a mode, the lock is first released and then a
new lock is obtained.
This function returns \e true if, after it executes, the file is
locked by this object, and \e false otherwise.
\sa unlock(), isLocked(), lockMode()
*/
/*!
\fn bool QtLockedFile::unlock()
Releases a lock.
If the object has no lock, this function returns immediately.
This function returns \e true if, after it executes, the file is
not locked by this object, and \e false otherwise.
\sa lock(), isLocked(), lockMode()
*/
/*!
\fn QtLockedFile::~QtLockedFile()
Destroys the \e QtLockedFile object. If any locks were held, they
are released.
*/

View File

@@ -1,96 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/
#ifndef QTLOCKEDFILE_H
#define QTLOCKEDFILE_H
#include <QtCore/QFile>
#ifdef Q_OS_WIN
#include <QtCore/QVector>
#endif
#if defined(Q_WS_WIN)
# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT)
# define QT_QTLOCKEDFILE_EXPORT
# elif defined(QT_QTLOCKEDFILE_IMPORT)
# if defined(QT_QTLOCKEDFILE_EXPORT)
# undef QT_QTLOCKEDFILE_EXPORT
# endif
# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport)
# elif defined(QT_QTLOCKEDFILE_EXPORT)
# undef QT_QTLOCKEDFILE_EXPORT
# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport)
# endif
#else
# define QT_QTLOCKEDFILE_EXPORT
#endif
namespace QtLP_Private {
class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
{
public:
enum LockMode { NoLock = 0, ReadLock, WriteLock };
QtLockedFile();
QtLockedFile(const QString &name);
~QtLockedFile();
bool open(OpenMode mode);
bool lock(LockMode mode, bool block = true);
bool unlock();
bool isLocked() const;
LockMode lockMode() const;
private:
#ifdef Q_OS_WIN
Qt::HANDLE wmutex;
Qt::HANDLE rmutex;
QVector<Qt::HANDLE> rmutexes;
QString mutexname;
Qt::HANDLE getMutexHandle(int idx, bool doCreate);
bool waitMutex(Qt::HANDLE mutex, bool doBlock);
#endif
LockMode m_lock_mode;
};
}
#endif

View File

@@ -1,114 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include "qtlockedfile.h"
bool QtLockedFile::lock(LockMode mode, bool block)
{
if (!isOpen()) {
qWarning("QtLockedFile::lock(): file is not opened");
return false;
}
if (mode == NoLock)
return unlock();
if (mode == m_lock_mode)
return true;
if (m_lock_mode != NoLock)
unlock();
struct flock fl;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK;
int cmd = block ? F_SETLKW : F_SETLK;
int ret = fcntl(handle(), cmd, &fl);
if (ret == -1) {
if (errno != EINTR && errno != EAGAIN)
qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
return false;
}
m_lock_mode = mode;
return true;
}
bool QtLockedFile::unlock()
{
if (!isOpen()) {
qWarning("QtLockedFile::unlock(): file is not opened");
return false;
}
if (!isLocked())
return true;
struct flock fl;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
fl.l_type = F_UNLCK;
int ret = fcntl(handle(), F_SETLKW, &fl);
if (ret == -1) {
qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
return false;
}
m_lock_mode = NoLock;
return true;
}
QtLockedFile::~QtLockedFile()
{
if (isOpen())
unlock();
}

View File

@@ -1,208 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/
#include "qtlockedfile.h"
#include <qt_windows.h>
#include <QtCore/QFileInfo>
#define MUTEX_PREFIX "QtLockedFile mutex "
// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS
#define MAX_READERS MAXIMUM_WAIT_OBJECTS
#define TCHAR WCHAR
Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate)
{
if (mutexname.isEmpty()) {
QFileInfo fi(*this);
mutexname = QString::fromLatin1(MUTEX_PREFIX)
+ fi.absoluteFilePath().toLower();
}
QString mname(mutexname);
if (idx >= 0)
mname += QString::number(idx);
Qt::HANDLE mutex;
if (doCreate) {
QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); },
{ mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } );
if (!mutex) {
qErrnoWarning("QtLockedFile::lock(): CreateMutex failed");
return 0;
}
}
else {
QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); },
{ mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } );
if (!mutex) {
if (GetLastError() != ERROR_FILE_NOT_FOUND)
qErrnoWarning("QtLockedFile::lock(): OpenMutex failed");
return 0;
}
}
return mutex;
}
bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock)
{
Q_ASSERT(mutex);
DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0);
switch (res) {
case WAIT_OBJECT_0:
case WAIT_ABANDONED:
return true;
break;
case WAIT_TIMEOUT:
break;
default:
qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed");
}
return false;
}
bool QtLockedFile::lock(LockMode mode, bool block)
{
if (!isOpen()) {
qWarning("QtLockedFile::lock(): file is not opened");
return false;
}
if (mode == NoLock)
return unlock();
if (mode == m_lock_mode)
return true;
if (m_lock_mode != NoLock)
unlock();
if (!wmutex && !(wmutex = getMutexHandle(-1, true)))
return false;
if (!waitMutex(wmutex, block))
return false;
if (mode == ReadLock) {
int idx = 0;
for (; idx < MAX_READERS; idx++) {
rmutex = getMutexHandle(idx, false);
if (!rmutex || waitMutex(rmutex, false))
break;
CloseHandle(rmutex);
}
bool ok = true;
if (idx >= MAX_READERS) {
qWarning("QtLockedFile::lock(): too many readers");
rmutex = 0;
ok = false;
}
else if (!rmutex) {
rmutex = getMutexHandle(idx, true);
if (!rmutex || !waitMutex(rmutex, false))
ok = false;
}
if (!ok && rmutex) {
CloseHandle(rmutex);
rmutex = 0;
}
ReleaseMutex(wmutex);
if (!ok)
return false;
}
else {
Q_ASSERT(rmutexes.isEmpty());
for (int i = 0; i < MAX_READERS; i++) {
Qt::HANDLE mutex = getMutexHandle(i, false);
if (mutex)
rmutexes.append(mutex);
}
if (rmutexes.size()) {
DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(),
TRUE, block ? INFINITE : 0);
if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) {
if (res != WAIT_TIMEOUT)
qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed");
m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky
unlock();
return false;
}
}
}
m_lock_mode = mode;
return true;
}
bool QtLockedFile::unlock()
{
if (!isOpen()) {
qWarning("QtLockedFile::unlock(): file is not opened");
return false;
}
if (!isLocked())
return true;
if (m_lock_mode == ReadLock) {
ReleaseMutex(rmutex);
CloseHandle(rmutex);
rmutex = 0;
}
else {
foreach(Qt::HANDLE mutex, rmutexes) {
ReleaseMutex(mutex);
CloseHandle(mutex);
}
rmutexes.clear();
ReleaseMutex(wmutex);
}
m_lock_mode = QtLockedFile::NoLock;
return true;
}
QtLockedFile::~QtLockedFile()
{
if (isOpen())
unlock();
if (wmutex)
CloseHandle(wmutex);
}

View File

@@ -1,344 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/
#include "qtsingleapplication.h"
#include "qtlocalpeer.h"
#include <QtGui/QWidget>
/*!
\class QtSingleApplication qtsingleapplication.h
\brief The QtSingleApplication class provides an API to detect and
communicate with running instances of an application.
This class allows you to create applications where only one
instance should be running at a time. I.e., if the user tries to
launch another instance, the already running instance will be
activated instead. Another usecase is a client-server system,
where the first started instance will assume the role of server,
and the later instances will act as clients of that server.
By default, the full path of the executable file is used to
determine whether two processes are instances of the same
application. You can also provide an explicit identifier string
that will be compared instead.
The application should create the QtSingleApplication object early
in the startup phase, and call isRunning() to find out if another
instance of this application is already running. If isRunning()
returns false, it means that no other instance is running, and
this instance has assumed the role as the running instance. In
this case, the application should continue with the initialization
of the application user interface before entering the event loop
with exec(), as normal.
The messageReceived() signal will be emitted when the running
application receives messages from another instance of the same
application. When a message is received it might be helpful to the
user to raise the application so that it becomes visible. To
facilitate this, QtSingleApplication provides the
setActivationWindow() function and the activateWindow() slot.
If isRunning() returns true, another instance is already
running. It may be alerted to the fact that another instance has
started by using the sendMessage() function. Also data such as
startup parameters (e.g. the name of the file the user wanted this
new instance to open) can be passed to the running instance with
this function. Then, the application should terminate (or enter
client mode).
If isRunning() returns true, but sendMessage() fails, that is an
indication that the running instance is frozen.
Here's an example that shows how to convert an existing
application to use QtSingleApplication. It is very simple and does
not make use of all QtSingleApplication's functionality (see the
examples for that).
\code
// Original
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MyMainWidget mmw;
mmw.show();
return app.exec();
}
// Single instance
int main(int argc, char **argv)
{
QtSingleApplication app(argc, argv);
if (app.isRunning())
return !app.sendMessage(someDataString);
MyMainWidget mmw;
app.setActivationWindow(&mmw);
mmw.show();
return app.exec();
}
\endcode
Once this QtSingleApplication instance is destroyed (normally when
the process exits or crashes), when the user next attempts to run the
application this instance will not, of course, be encountered. The
next instance to call isRunning() or sendMessage() will assume the
role as the new running instance.
For console (non-GUI) applications, QtSingleCoreApplication may be
used instead of this class, to avoid the dependency on the QtGui
library.
\sa QtSingleCoreApplication
*/
void QtSingleApplication::sysInit(const QString &appId)
{
actWin = 0;
peer = new QtLocalPeer(this, appId);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
}
/*!
Creates a QtSingleApplication object. The application identifier
will be QCoreApplication::applicationFilePath(). \a argc, \a
argv, and \a GUIenabled are passed on to the QAppliation constructor.
If you are creating a console application (i.e. setting \a
GUIenabled to false), you may consider using
QtSingleCoreApplication instead.
*/
QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled)
: QApplication(argc, argv, GUIenabled)
{
sysInit();
}
/*!
Creates a QtSingleApplication object with the application
identifier \a appId. \a argc and \a argv are passed on to the
QAppliation constructor.
*/
QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
: QApplication(argc, argv)
{
sysInit(appId);
}
/*!
Creates a QtSingleApplication object. The application identifier
will be QCoreApplication::applicationFilePath(). \a argc, \a
argv, and \a type are passed on to the QAppliation constructor.
*/
QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type)
: QApplication(argc, argv, type)
{
sysInit();
}
#if defined(Q_WS_X11)
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be QCoreApplication::applicationFilePath(). \a dpy, \a visual,
and \a cmap are passed on to the QApplication constructor.
*/
QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, visual, cmap)
{
sysInit();
}
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a
argv, \a visual, and \a cmap are passed on to the QApplication
constructor.
*/
QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, argc, argv, visual, cmap)
{
sysInit();
}
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be \a appId. \a dpy, \a argc, \a
argv, \a visual, and \a cmap are passed on to the QApplication
constructor.
*/
QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, argc, argv, visual, cmap)
{
sysInit(appId);
}
#endif
/*!
Returns true if another instance of this application is running;
otherwise false.
This function does not find instances of this application that are
being run by a different user (on Windows: that are running in
another session).
\sa sendMessage()
*/
bool QtSingleApplication::isRunning()
{
return peer->isClient();
}
/*!
Tries to send the text \a message to the currently running
instance. The QtSingleApplication object in the running instance
will emit the messageReceived() signal when it receives the
message.
This function returns true if the message has been sent to, and
processed by, the current instance. If there is no instance
currently running, or if the running instance fails to process the
message within \a timeout milliseconds, this function return false.
\sa isRunning(), messageReceived()
*/
bool QtSingleApplication::sendMessage(const QString &message, int timeout)
{
return peer->sendMessage(message, timeout);
}
/*!
Returns the application identifier. Two processes with the same
identifier will be regarded as instances of the same application.
*/
QString QtSingleApplication::id() const
{
return peer->applicationId();
}
/*!
Sets the activation window of this application to \a aw. The
activation window is the widget that will be activated by
activateWindow(). This is typically the application's main window.
If \a activateOnMessage is true (the default), the window will be
activated automatically every time a message is received, just prior
to the messageReceived() signal being emitted.
\sa activateWindow(), messageReceived()
*/
void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage)
{
actWin = aw;
if (activateOnMessage)
connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
else
disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
}
/*!
Returns the applications activation window if one has been set by
calling setActivationWindow(), otherwise returns 0.
\sa setActivationWindow()
*/
QWidget* QtSingleApplication::activationWindow() const
{
return actWin;
}
/*!
De-minimizes, raises, and activates this application's activation window.
This function does nothing if no activation window has been set.
This is a convenience function to show the user that this
application instance has been activated when he has tried to start
another instance.
This function should typically be called in response to the
messageReceived() signal. By default, that will happen
automatically, if an activation window has been set.
\sa setActivationWindow(), messageReceived(), initialize()
*/
void QtSingleApplication::activateWindow()
{
if (actWin) {
actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
actWin->raise();
actWin->activateWindow();
}
}
/*!
\fn void QtSingleApplication::messageReceived(const QString& message)
This signal is emitted when the current instance receives a \a
message from another instance of this application.
\sa sendMessage(), setActivationWindow(), activateWindow()
*/
/*!
\fn void QtSingleApplication::initialize(bool dummy = true)
\obsolete
*/

View File

@@ -1,84 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/
#include <QtGui/QApplication>
#include "dllmacro.h"
class QtLocalPeer;
class DLLEXPORT QtSingleApplication : public QApplication
{
Q_OBJECT
public:
QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
QtSingleApplication(const QString &id, int &argc, char **argv);
QtSingleApplication(int &argc, char **argv, Type type);
#if defined(Q_WS_X11)
QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0);
QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
#endif
bool isRunning();
QString id() const;
void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
QWidget* activationWindow() const;
// Obsolete:
void initialize(bool dummy = true)
{ isRunning(); Q_UNUSED(dummy) }
public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000);
void activateWindow();
Q_SIGNALS:
void messageReceived(const QString &message);
private:
void sysInit(const QString &appId = QString());
QtLocalPeer *peer;
QWidget *actWin;
};

View File

@@ -1,148 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/
#include "qtsinglecoreapplication.h"
#include "qtlocalpeer.h"
/*!
\class QtSingleCoreApplication qtsinglecoreapplication.h
\brief A variant of the QtSingleApplication class for non-GUI applications.
This class is a variant of QtSingleApplication suited for use in
console (non-GUI) applications. It is an extension of
QCoreApplication (instead of QApplication). It does not require
the QtGui library.
The API and usage is identical to QtSingleApplication, except that
functions relating to the "activation window" are not present, for
obvious reasons. Please refer to the QtSingleApplication
documentation for explanation of the usage.
A QtSingleCoreApplication instance can communicate to a
QtSingleApplication instance if they share the same application
id. Hence, this class can be used to create a light-weight
command-line tool that sends commands to a GUI application.
\sa QtSingleApplication
*/
/*!
Creates a QtSingleCoreApplication object. The application identifier
will be QCoreApplication::applicationFilePath(). \a argc and \a
argv are passed on to the QCoreAppliation constructor.
*/
QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv)
: QCoreApplication(argc, argv)
{
peer = new QtLocalPeer(this);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
}
/*!
Creates a QtSingleCoreApplication object with the application
identifier \a appId. \a argc and \a argv are passed on to the
QCoreAppliation constructor.
*/
QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv)
: QCoreApplication(argc, argv)
{
peer = new QtLocalPeer(this, appId);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
}
/*!
Returns true if another instance of this application is running;
otherwise false.
This function does not find instances of this application that are
being run by a different user (on Windows: that are running in
another session).
\sa sendMessage()
*/
bool QtSingleCoreApplication::isRunning()
{
return peer->isClient();
}
/*!
Tries to send the text \a message to the currently running
instance. The QtSingleCoreApplication object in the running instance
will emit the messageReceived() signal when it receives the
message.
This function returns true if the message has been sent to, and
processed by, the current instance. If there is no instance
currently running, or if the running instance fails to process the
message within \a timeout milliseconds, this function return false.
\sa isRunning(), messageReceived()
*/
bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout)
{
return peer->sendMessage(message, timeout);
}
/*!
Returns the application identifier. Two processes with the same
identifier will be regarded as instances of the same application.
*/
QString QtSingleCoreApplication::id() const
{
return peer->applicationId();
}
/*!
\fn void QtSingleCoreApplication::messageReceived(const QString& message)
This signal is emitted when the current instance receives a \a
message from another instance of this application.
\sa sendMessage()
*/

View File

@@ -1,66 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/
#include <QtCore/QCoreApplication>
class QtLocalPeer;
class QtSingleCoreApplication : public QCoreApplication
{
Q_OBJECT
public:
QtSingleCoreApplication(int &argc, char **argv);
QtSingleCoreApplication(const QString &id, int &argc, char **argv);
bool isRunning();
QString id() const;
public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000);
Q_SIGNALS:
void messageReceived(const QString &message);
private:
QtLocalPeer* peer;
};

View File

@@ -63,7 +63,7 @@ Query::addResults( const QList< Tomahawk::result_ptr >& newresults )
{
bool becameSolved = false;
{
// QMutexLocker lock( &m_mut );
QMutexLocker lock( &m_mutex );
m_results.append( newresults );
qStableSort( m_results.begin(), m_results.end(), Query::resultSorter );
@@ -101,7 +101,7 @@ void
Query::removeResult( const Tomahawk::result_ptr& result )
{
{
// QMutexLocker lock( &m_mut );
QMutexLocker lock( &m_mutex );
m_results.removeAll( result );
}
@@ -121,7 +121,7 @@ Query::onResolvingFinished()
QList< result_ptr >
Query::results() const
{
// QMutexLocker lock( &m_mut );
QMutexLocker lock( &m_mutex );
return m_results;
}
@@ -129,7 +129,7 @@ Query::results() const
unsigned int
Query::numResults() const
{
// QMutexLocker lock( &m_mut );
QMutexLocker lock( &m_mutex );
return m_results.length();
}
@@ -149,7 +149,18 @@ Query::id() const
bool
Query::resultSorter( const result_ptr& left, const result_ptr& right )
{
return left->score() > right->score();
const float ls = left->score();
const float rs = right->score();
if ( ls == rs )
{
if ( !left->collection().isNull() && left->collection()->source()->isLocal() )
return true;
else
return false;
}
return ls > rs;
}

View File

@@ -116,6 +116,8 @@ private:
QString m_track;
int m_duration;
QString m_resultHint;
mutable QMutex m_mutex;
};
}; //ns

View File

@@ -38,7 +38,7 @@ Source::Source( int id, const QString &username )
, m_id( id )
, m_cc( 0 )
{
qDebug() << Q_FUNC_INFO;
qDebug() << Q_FUNC_INFO << id << username;
if ( id == 0 )
{
@@ -146,6 +146,7 @@ Source::setOnline()
{
if ( m_online )
return;
m_online = true;
// ensure username is in the database
DatabaseCommand_addSource* cmd = new DatabaseCommand_addSource( m_username, m_friendlyname );
@@ -153,7 +154,6 @@ Source::setOnline()
SLOT( dbLoaded( unsigned int, const QString& ) ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
m_online = true;
emit online();
}

View File

@@ -66,29 +66,50 @@ TomahawkSettings::~TomahawkSettings()
QStringList
TomahawkSettings::scannerPath() const
TomahawkSettings::scannerPaths()
{
//FIXME: After enough time, remove this hack (and make const)
#ifndef TOMAHAWK_HEADLESS
return value( "scannerpath", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toStringList();
if( value( "scannerpaths" ).isNull() )
setValue( "scannerpaths", value( "scannerpath" ) );
return value( "scannerpaths", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toStringList();
#else
return value( "scannerpath", "" ).toStringList();
if( value( "scannerpaths" ).isNull() )
setValue( "scannerpaths", value( "scannerpath" ) );
return value( "scannerpaths", "" ).toStringList();
#endif
}
void
TomahawkSettings::setScannerPath( const QStringList& path )
TomahawkSettings::setScannerPaths( const QStringList& paths )
{
setValue( "scannerpath", path );
setValue( "scannerpaths", paths );
}
bool
TomahawkSettings::hasScannerPath() const
TomahawkSettings::hasScannerPaths() const
{
return contains( "scannerpath" );
//FIXME: After enough time, remove this hack
return contains( "scannerpaths" ) || contains( "scannerpath" );
}
bool
TomahawkSettings::watchForChanges() const
{
return value( "watchForChanges", true ).toBool();
}
void
TomahawkSettings::setWatchForChanges( bool watch )
{
setValue( "watchForChanges", watch );
}
void
TomahawkSettings::setAcceptedLegalWarning( bool accept )
{

View File

@@ -41,9 +41,12 @@ public:
void applyChanges() { emit changed(); }
/// General settings
QStringList scannerPath() const; /// QDesktopServices::MusicLocation by default
void setScannerPath( const QStringList& path );
bool hasScannerPath() const;
QStringList scannerPaths(); /// QDesktopServices::MusicLocation by default
void setScannerPaths( const QStringList& paths );
bool hasScannerPaths() const;
bool watchForChanges() const;
void setWatchForChanges( bool watch );
bool acceptedLegalWarning() const;
void setAcceptedLegalWarning( bool accept );

View File

@@ -83,7 +83,7 @@ AnimatedSplitter::hide( int index, bool animate )
emit hidden( w );
w->setMinimumHeight( minHeight );
qDebug() << "animating to:" << w->height() << "from" << minHeight;
// qDebug() << "animating to:" << w->height() << "from" << minHeight;
m_animateForward = false;
if ( animate )

View File

@@ -31,7 +31,9 @@
#include <QPainter>
#define FILTER_TIMEOUT 280
#define HISTORY_TRACK_ITEMS 50
#define HISTORY_PLAYLIST_ITEMS 10
#define HISTORY_RESOLVING_TIMEOUT 2500
WelcomeWidget::WelcomeWidget( QWidget* parent )
@@ -46,7 +48,10 @@ WelcomeWidget::WelcomeWidget( QWidget* parent )
m_tracksModel = new PlaylistModel( ui->tracksView );
ui->tracksView->setModel( m_tracksModel );
m_tracksModel->loadHistory( Tomahawk::source_ptr() );
m_tracksModel->loadHistory( Tomahawk::source_ptr(), HISTORY_TRACK_ITEMS );
m_timer = new QTimer( this );
connect( m_timer, SIGNAL( timeout() ), SLOT( checkQueries() ) );
connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) );
@@ -96,10 +101,25 @@ WelcomeWidget::onSourceAdded( const Tomahawk::source_ptr& source )
}
void
WelcomeWidget::checkQueries()
{
m_timer->stop();
m_tracksModel->ensureResolved();
}
void
WelcomeWidget::onPlaybackFinished( const Tomahawk::query_ptr& query )
{
m_tracksModel->insert( 0, query );
if ( m_tracksModel->trackCount() > HISTORY_TRACK_ITEMS )
m_tracksModel->remove( HISTORY_TRACK_ITEMS );
if ( m_timer->isActive() )
m_timer->stop();
m_timer->start( HISTORY_RESOLVING_TIMEOUT );
}

View File

@@ -124,10 +124,13 @@ private slots:
void onPlaylistActivated( QListWidgetItem* item );
void onPlaybackFinished( const Tomahawk::query_ptr& query );
void checkQueries();
private:
Ui::WelcomeWidget *ui;
PlaylistModel* m_tracksModel;
QTimer* m_timer;
};
#endif // WELCOMEWIDGET_H

View File

@@ -18,13 +18,16 @@
#include "tomahawk/tomahawkapp.h"
#include "kdsingleapplicationguard/kdsingleapplicationguard.h"
#include <QTranslator>
#ifdef Q_WS_MAC
#include "tomahawkapp_mac.h"
#include </System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/Headers/AppleEvents.h>
static pascal OSErr appleEventHandler( const AppleEvent*, AppleEvent*, long );
#endif
#include <exception>
int
main( int argc, char *argv[] )
{
@@ -36,18 +39,20 @@ main( int argc, char *argv[] )
// used for url handler
AEEventHandlerUPP h = AEEventHandlerUPP( appleEventHandler );
AEInstallEventHandler( 'GURL', 'GURL', h, 0, false );
#endif
try
{
TomahawkApp a( argc, argv );
KDSingleApplicationGuard guard( &a, KDSingleApplicationGuard::AutoKillOtherInstances );
QObject::connect( &guard, SIGNAL( instanceStarted( KDSingleApplicationGuard::Instance ) ), &a, SLOT( instanceStarted( KDSingleApplicationGuard::Instance ) ) );
QString locale = QLocale::system().name();
QTranslator translator;
translator.load( QString( ":/lang/tomahawk_" ) + locale );
a.installTranslator( &translator );
return a.exec();
}
catch( const std::runtime_error& e )
{
return 0;
}
}
#ifdef Q_WS_MAC
static pascal OSErr

Some files were not shown because too many files have changed in this diff Show More