@@ -128,7 +128,7 @@ No QRCode found. Try to zoom in or move it to the center of the screen.,QRCode | |||||
Shadowsocks is already running.,Shadowsocks уже запущен.,Shadowsocks 已经在运行。,Shadowsocks 已經在執行。,Shadowsocks 実行中,Shadowsocks가 이미 실행 중입니다.,Shadowsocks est déjà en cours d'exécution. | Shadowsocks is already running.,Shadowsocks уже запущен.,Shadowsocks 已经在运行。,Shadowsocks 已經在執行。,Shadowsocks 実行中,Shadowsocks가 이미 실행 중입니다.,Shadowsocks est déjà en cours d'exécution. | ||||
Find Shadowsocks icon in your notify tray.,Значок Shadowsocks можно найти в области уведомлений.,请在任务栏里寻找 Shadowsocks 图标。,請在工作列裡尋找 Shadowsocks 圖示。,通知領域には Shadowsocks のアイコンがあります。,트레이에서 Shadowsocks를 찾아주세요.,Trouvez l'icône Shadowsocks dans votre barre de notification. | Find Shadowsocks icon in your notify tray.,Значок Shadowsocks можно найти в области уведомлений.,请在任务栏里寻找 Shadowsocks 图标。,請在工作列裡尋找 Shadowsocks 圖示。,通知領域には Shadowsocks のアイコンがあります。,트레이에서 Shadowsocks를 찾아주세요.,Trouvez l'icône Shadowsocks dans votre barre de notification. | ||||
"If you want to start multiple Shadowsocks, make a copy in another directory.","Если вы хотите запустить несколько копий Shadowsocks одновременно, создайте отдельную папку на каждую копию.",如果想同时启动多个,可以另外复制一份到别的目录。,如果想同時啟動多個,可以另外複製一份至別的目錄。,複数起動したい場合は、プログラムファイルを別のフォルダーにコピーしてから、もう一度実行して下さい。,"만약 여러 개의 Shadowsocks를 사용하고 싶으시다면, 파일을 다른 곳에 복사해주세요.","Si vous souhaitez démarrer plusieurs Shadowsocks, faites une copie dans un autre répertoire." | "If you want to start multiple Shadowsocks, make a copy in another directory.","Если вы хотите запустить несколько копий Shadowsocks одновременно, создайте отдельную папку на каждую копию.",如果想同时启动多个,可以另外复制一份到别的目录。,如果想同時啟動多個,可以另外複製一份至別的目錄。,複数起動したい場合は、プログラムファイルを別のフォルダーにコピーしてから、もう一度実行して下さい。,"만약 여러 개의 Shadowsocks를 사용하고 싶으시다면, 파일을 다른 곳에 복사해주세요.","Si vous souhaitez démarrer plusieurs Shadowsocks, faites une copie dans un autre répertoire." | ||||
Failed to decode QRCode,Не удалось распознать QRCode,无法解析二维码,QR 碼解碼失敗,QR コードの読み取りに失敗しました。,QR코드를 해석하는데에 실패했습니다.,Impossible de décoder le QRCode | |||||
Invalid QR Code content: {0},,无效二维码内容: {0},無效二維碼內容: {0},,, | |||||
Failed to update registry,Не удалось обновить запись в реестре,无法修改注册表,無法修改登錄檔,レジストリの更新に失敗しました。,레지스트리를 업데이트하는데에 실패했습니다.,Impossible de mettre à jour de la base de registre | Failed to update registry,Не удалось обновить запись в реестре,无法修改注册表,無法修改登錄檔,レジストリの更新に失敗しました。,레지스트리를 업데이트하는데에 실패했습니다.,Impossible de mettre à jour de la base de registre | ||||
Import from URL: {0} ?,импортировать из адреса: {0} ?,从URL导入: {0} ?,從URL匯入: {0} ?,{0}:このURLからインポートしますか?,, | Import from URL: {0} ?,импортировать из адреса: {0} ?,从URL导入: {0} ?,從URL匯入: {0} ?,{0}:このURLからインポートしますか?,, | ||||
Successfully imported from {0},Успешно импортировано из {0},导入成功:{0},導入成功:{0},{0}:インポートしました。,, | Successfully imported from {0},Успешно импортировано из {0},导入成功:{0},導入成功:{0},{0}:インポートしました。,, | ||||
@@ -8,6 +8,10 @@ using System.Windows.Forms; | |||||
using Microsoft.Win32; | using Microsoft.Win32; | ||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using System.Drawing; | |||||
using ZXing; | |||||
using ZXing.QrCode; | |||||
using ZXing.Common; | |||||
namespace Shadowsocks.Util | namespace Shadowsocks.Util | ||||
{ | { | ||||
@@ -227,11 +231,47 @@ namespace Shadowsocks.Util | |||||
return Environment.OSVersion.Version.Major > 5; | return Environment.OSVersion.Version.Major > 5; | ||||
} | } | ||||
[DllImport("kernel32.dll")] | |||||
[return: MarshalAs(UnmanagedType.Bool)] | |||||
private static extern bool SetProcessWorkingSetSize(IntPtr process, | |||||
UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize); | |||||
public static string ScanQRCodeFromScreen() | |||||
{ | |||||
foreach (Screen screen in Screen.AllScreens) | |||||
{ | |||||
using (Bitmap fullImage = new Bitmap(screen.Bounds.Width, | |||||
screen.Bounds.Height)) | |||||
{ | |||||
using (Graphics g = Graphics.FromImage(fullImage)) | |||||
{ | |||||
g.CopyFromScreen(screen.Bounds.X, | |||||
screen.Bounds.Y, | |||||
0, 0, | |||||
fullImage.Size, | |||||
CopyPixelOperation.SourceCopy); | |||||
} | |||||
int maxTry = 10; | |||||
for (int i = 0; i < maxTry; i++) | |||||
{ | |||||
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry); | |||||
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry); | |||||
Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2); | |||||
Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height); | |||||
double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width; | |||||
using (Graphics g = Graphics.FromImage(target)) | |||||
{ | |||||
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height), | |||||
cropRect, | |||||
GraphicsUnit.Pixel); | |||||
} | |||||
var source = new BitmapLuminanceSource(target); | |||||
var bitmap = new BinaryBitmap(new HybridBinarizer(source)); | |||||
QRCodeReader reader = new QRCodeReader(); | |||||
var result = reader.decode(bitmap); | |||||
if (result != null) | |||||
return result.Text; | |||||
} | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
// See: https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx | // See: https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx | ||||
public static bool IsSupportedRuntimeVersion() | public static bool IsSupportedRuntimeVersion() | ||||
@@ -714,12 +714,7 @@ namespace Shadowsocks.View | |||||
ShowConfigForm(); | ShowConfigForm(); | ||||
} | } | ||||
void splash_FormClosed(object sender, FormClosedEventArgs e) | |||||
{ | |||||
ShowConfigForm(); | |||||
} | |||||
void openURLFromQRCode(object sender, FormClosedEventArgs e) | |||||
void openURLFromQRCode() | |||||
{ | { | ||||
Process.Start(_urlToOpen); | Process.Start(_urlToOpen); | ||||
} | } | ||||
@@ -751,86 +746,26 @@ namespace Shadowsocks.View | |||||
private void ScanQRCodeItem_Click(object sender, EventArgs e) | private void ScanQRCodeItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
foreach (Screen screen in Screen.AllScreens) | |||||
var result = Utils.ScanQRCodeFromScreen(); | |||||
if (result != null) | |||||
{ | { | ||||
using (Bitmap fullImage = new Bitmap(screen.Bounds.Width, | |||||
screen.Bounds.Height)) | |||||
if (result.ToLowerInvariant().StartsWith("http://") || result.ToLowerInvariant().StartsWith("https://")) | |||||
{ | { | ||||
using (Graphics g = Graphics.FromImage(fullImage)) | |||||
{ | |||||
g.CopyFromScreen(screen.Bounds.X, | |||||
screen.Bounds.Y, | |||||
0, 0, | |||||
fullImage.Size, | |||||
CopyPixelOperation.SourceCopy); | |||||
} | |||||
int maxTry = 10; | |||||
for (int i = 0; i < maxTry; i++) | |||||
{ | |||||
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry); | |||||
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry); | |||||
Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2); | |||||
Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height); | |||||
double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width; | |||||
using (Graphics g = Graphics.FromImage(target)) | |||||
{ | |||||
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height), | |||||
cropRect, | |||||
GraphicsUnit.Pixel); | |||||
} | |||||
var source = new BitmapLuminanceSource(target); | |||||
var bitmap = new BinaryBitmap(new HybridBinarizer(source)); | |||||
QRCodeReader reader = new QRCodeReader(); | |||||
var result = reader.decode(bitmap); | |||||
if (result != null) | |||||
{ | |||||
var success = controller.AddServerBySSURL(result.Text); | |||||
QRCodeSplashForm splash = new QRCodeSplashForm(); | |||||
if (success) | |||||
{ | |||||
splash.FormClosed += splash_FormClosed; | |||||
} | |||||
else if (result.Text.ToLower().StartsWith("http://") || result.Text.ToLower().StartsWith("https://")) | |||||
{ | |||||
_urlToOpen = result.Text; | |||||
splash.FormClosed += openURLFromQRCode; | |||||
} | |||||
else | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Failed to decode QRCode")); | |||||
return; | |||||
} | |||||
double minX = Int32.MaxValue, minY = Int32.MaxValue, maxX = 0, maxY = 0; | |||||
foreach (ResultPoint point in result.ResultPoints) | |||||
{ | |||||
minX = Math.Min(minX, point.X); | |||||
minY = Math.Min(minY, point.Y); | |||||
maxX = Math.Max(maxX, point.X); | |||||
maxY = Math.Max(maxY, point.Y); | |||||
} | |||||
minX /= imageScale; | |||||
minY /= imageScale; | |||||
maxX /= imageScale; | |||||
maxY /= imageScale; | |||||
// make it 20% larger | |||||
double margin = (maxX - minX) * 0.20f; | |||||
minX += -margin + marginLeft; | |||||
maxX += margin + marginLeft; | |||||
minY += -margin + marginTop; | |||||
maxY += margin + marginTop; | |||||
splash.Location = new Point(screen.Bounds.X, screen.Bounds.Y); | |||||
// we need a panel because a window has a minimal size | |||||
// TODO: test on high DPI | |||||
splash.TargetRect = new Rectangle((int)minX, (int)minY, (int)maxX - (int)minX, (int)maxY - (int)minY); | |||||
splash.Size = new Size(fullImage.Width, fullImage.Height); | |||||
splash.Show(); | |||||
return; | |||||
} | |||||
} | |||||
_urlToOpen = result; | |||||
openURLFromQRCode(); | |||||
} | } | ||||
else if (controller.AddServerBySSURL(result)) | |||||
{ | |||||
ShowConfigForm(); | |||||
} | |||||
else | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Invalid QR Code content: {0}", result)); | |||||
} | |||||
return; | |||||
} | } | ||||
MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen.")); | |||||
else | |||||
MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen.")); | |||||
} | } | ||||
private void ImportURLItem_Click(object sender, EventArgs e) | private void ImportURLItem_Click(object sender, EventArgs e) | ||||
@@ -1,281 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel; | |||||
using System.Data; | |||||
using System.Drawing; | |||||
using System.Text; | |||||
using System.Windows.Forms; | |||||
using System.Drawing.Imaging; | |||||
using System.Runtime.InteropServices; | |||||
using System.Diagnostics; | |||||
namespace Shadowsocks.View | |||||
{ | |||||
public class QRCodeSplashForm : Form | |||||
{ | |||||
public Rectangle TargetRect; | |||||
public QRCodeSplashForm() | |||||
{ | |||||
FormBorderStyle = FormBorderStyle.None; | |||||
this.Load += QRCodeSplashForm_Load; | |||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; | |||||
this.BackColor = System.Drawing.Color.White; | |||||
this.ClientSize = new System.Drawing.Size(1, 1); | |||||
this.ControlBox = false; | |||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; | |||||
this.MaximizeBox = false; | |||||
this.MinimizeBox = false; | |||||
this.Name = "QRCodeSplashForm"; | |||||
this.ShowIcon = false; | |||||
this.ShowInTaskbar = false; | |||||
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; | |||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; | |||||
this.TopMost = true; | |||||
} | |||||
private Timer timer; | |||||
private int flashStep; | |||||
private static double FPS = 1.0 / 15 * 1000; // System.Windows.Forms.Timer resolution is 15ms | |||||
private static double ANIMATION_TIME = 0.5; | |||||
private static int ANIMATION_STEPS = (int)(ANIMATION_TIME * FPS); | |||||
Stopwatch sw; | |||||
int x; | |||||
int y; | |||||
int w; | |||||
int h; | |||||
Bitmap bitmap; | |||||
Graphics g; | |||||
Pen pen; | |||||
SolidBrush brush; | |||||
private void QRCodeSplashForm_Load(object sender, EventArgs e) | |||||
{ | |||||
SetStyle(ControlStyles.SupportsTransparentBackColor, true); | |||||
this.BackColor = Color.Transparent; | |||||
flashStep = 0; | |||||
x = 0; | |||||
y = 0; | |||||
w = Width; | |||||
h = Height; | |||||
sw = Stopwatch.StartNew(); | |||||
timer = new Timer(); | |||||
timer.Interval = (int)(ANIMATION_TIME * 1000 / ANIMATION_STEPS); | |||||
timer.Tick += timer_Tick; | |||||
timer.Start(); | |||||
bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); | |||||
g = Graphics.FromImage(bitmap); | |||||
pen = new Pen(Color.Red, 3); | |||||
brush = new SolidBrush(Color.FromArgb(30, Color.Red)); | |||||
} | |||||
void timer_Tick(object sender, EventArgs e) | |||||
{ | |||||
double percent = (double)sw.ElapsedMilliseconds / 1000.0 / (double)ANIMATION_TIME; | |||||
if (percent < 1) | |||||
{ | |||||
// ease out | |||||
percent = 1 - Math.Pow((1 - percent), 4); | |||||
x = (int)(TargetRect.X * percent); | |||||
y = (int)(TargetRect.Y * percent); | |||||
w = (int)(TargetRect.Width * percent + this.Size.Width * (1 - percent)); | |||||
h = (int)(TargetRect.Height * percent + this.Size.Height * (1 - percent)); | |||||
//codeRectView.Location = new Point(x, y); | |||||
//codeRectView.Size = new Size(w, h); | |||||
pen.Color = Color.FromArgb((int)(255 * percent), Color.Red); | |||||
brush.Color = Color.FromArgb((int)(30 * percent), Color.Red); | |||||
g.Clear(Color.Transparent); | |||||
g.FillRectangle(brush, x, y, w, h); | |||||
g.DrawRectangle(pen, x, y, w, h); | |||||
SetBitmap(bitmap); | |||||
} | |||||
else | |||||
{ | |||||
if (flashStep == 0) | |||||
{ | |||||
timer.Interval = 100; | |||||
g.Clear(Color.Transparent); | |||||
SetBitmap(bitmap); | |||||
} | |||||
else if (flashStep == 1) | |||||
{ | |||||
timer.Interval = 50; | |||||
g.FillRectangle(brush, x, y, w, h); | |||||
g.DrawRectangle(pen, x, y, w, h); | |||||
SetBitmap(bitmap); | |||||
} | |||||
else if (flashStep == 2) | |||||
{ | |||||
g.Clear(Color.Transparent); | |||||
SetBitmap(bitmap); | |||||
} | |||||
else if (flashStep == 3) | |||||
{ | |||||
g.FillRectangle(brush, x, y, w, h); | |||||
g.DrawRectangle(pen, x, y, w, h); | |||||
SetBitmap(bitmap); | |||||
} | |||||
else if (flashStep == 4) | |||||
{ | |||||
g.Clear(Color.Transparent); | |||||
SetBitmap(bitmap); | |||||
} | |||||
else if (flashStep == 5) | |||||
{ | |||||
g.FillRectangle(brush, x, y, w, h); | |||||
g.DrawRectangle(pen, x, y, w, h); | |||||
SetBitmap(bitmap); | |||||
} | |||||
else | |||||
{ | |||||
sw.Stop(); | |||||
timer.Stop(); | |||||
pen.Dispose(); | |||||
brush.Dispose(); | |||||
bitmap.Dispose(); | |||||
this.Close(); | |||||
} | |||||
flashStep++; | |||||
} | |||||
} | |||||
// PerPixelAlphaForm.cs | |||||
// http://www.codeproject.com/Articles/1822/Per-Pixel-Alpha-Blend-in-C | |||||
// Rui Lopes | |||||
protected override CreateParams CreateParams | |||||
{ | |||||
get | |||||
{ | |||||
CreateParams cp = base.CreateParams; | |||||
cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style | |||||
return cp; | |||||
} | |||||
} | |||||
public void SetBitmap(Bitmap bitmap) | |||||
{ | |||||
SetBitmap(bitmap, 255); | |||||
} | |||||
/// <para>Changes the current bitmap with a custom opacity level. Here is where all happens!</para> | |||||
public void SetBitmap(Bitmap bitmap, byte opacity) | |||||
{ | |||||
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) | |||||
throw new ApplicationException("The bitmap must be 32ppp with alpha-channel."); | |||||
// The idea of this is very simple, | |||||
// 1. Create a compatible DC with screen; | |||||
// 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC; | |||||
// 3. Call the UpdateLayeredWindow. | |||||
IntPtr screenDc = Win32.GetDC(IntPtr.Zero); | |||||
IntPtr memDc = Win32.CreateCompatibleDC(screenDc); | |||||
IntPtr hBitmap = IntPtr.Zero; | |||||
IntPtr oldBitmap = IntPtr.Zero; | |||||
try | |||||
{ | |||||
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap | |||||
oldBitmap = Win32.SelectObject(memDc, hBitmap); | |||||
Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height); | |||||
Win32.Point pointSource = new Win32.Point(0, 0); | |||||
Win32.Point topPos = new Win32.Point(Left, Top); | |||||
Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION(); | |||||
blend.BlendOp = Win32.AC_SRC_OVER; | |||||
blend.BlendFlags = 0; | |||||
blend.SourceConstantAlpha = opacity; | |||||
blend.AlphaFormat = Win32.AC_SRC_ALPHA; | |||||
Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA); | |||||
} | |||||
finally | |||||
{ | |||||
Win32.ReleaseDC(IntPtr.Zero, screenDc); | |||||
if (hBitmap != IntPtr.Zero) | |||||
{ | |||||
Win32.SelectObject(memDc, oldBitmap); | |||||
//Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the normal DeleteObject from Win32 GDI and it's working fine without any resource leak. | |||||
Win32.DeleteObject(hBitmap); | |||||
} | |||||
Win32.DeleteDC(memDc); | |||||
} | |||||
} | |||||
} | |||||
// class that exposes needed win32 gdi functions. | |||||
class Win32 | |||||
{ | |||||
[StructLayout(LayoutKind.Sequential)] | |||||
public struct Point | |||||
{ | |||||
public Int32 x; | |||||
public Int32 y; | |||||
public Point(Int32 x, Int32 y) { this.x = x; this.y = y; } | |||||
} | |||||
[StructLayout(LayoutKind.Sequential)] | |||||
public struct Size | |||||
{ | |||||
public Int32 cx; | |||||
public Int32 cy; | |||||
public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; } | |||||
} | |||||
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |||||
struct ARGB | |||||
{ | |||||
public byte Blue; | |||||
public byte Green; | |||||
public byte Red; | |||||
public byte Alpha; | |||||
} | |||||
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |||||
public struct BLENDFUNCTION | |||||
{ | |||||
public byte BlendOp; | |||||
public byte BlendFlags; | |||||
public byte SourceConstantAlpha; | |||||
public byte AlphaFormat; | |||||
} | |||||
public const Int32 ULW_COLORKEY = 0x00000001; | |||||
public const Int32 ULW_ALPHA = 0x00000002; | |||||
public const Int32 ULW_OPAQUE = 0x00000004; | |||||
public const byte AC_SRC_OVER = 0x00; | |||||
public const byte AC_SRC_ALPHA = 0x01; | |||||
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] | |||||
public static extern int UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags); | |||||
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] | |||||
public static extern IntPtr GetDC(IntPtr hWnd); | |||||
[DllImport("user32.dll", ExactSpelling = true)] | |||||
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); | |||||
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] | |||||
public static extern IntPtr CreateCompatibleDC(IntPtr hDC); | |||||
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] | |||||
public static extern int DeleteDC(IntPtr hdc); | |||||
[DllImport("gdi32.dll", ExactSpelling = true)] | |||||
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); | |||||
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] | |||||
public static extern int DeleteObject(IntPtr hObject); | |||||
} | |||||
} |
@@ -332,9 +332,6 @@ | |||||
<DependentUpon>LogForm.cs</DependentUpon> | <DependentUpon>LogForm.cs</DependentUpon> | ||||
</Compile> | </Compile> | ||||
<Compile Include="View\MenuViewController.cs" /> | <Compile Include="View\MenuViewController.cs" /> | ||||
<Compile Include="View\QRCodeSplashForm.cs"> | |||||
<SubType>Form</SubType> | |||||
</Compile> | |||||
<Compile Include="Views\ServerSharingView.xaml.cs"> | <Compile Include="Views\ServerSharingView.xaml.cs"> | ||||
<DependentUpon>ServerSharingView.xaml</DependentUpon> | <DependentUpon>ServerSharingView.xaml</DependentUpon> | ||||
</Compile> | </Compile> | ||||