diff --git a/Niv.csproj b/Niv.csproj
index a947af5..a0570c1 100644
--- a/Niv.csproj
+++ b/Niv.csproj
@@ -85,6 +85,8 @@
+
+
@@ -187,6 +189,11 @@
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/exe/Niv.exe b/exe/Niv.exe
index e217ee2..f5b5f23 100644
Binary files a/exe/Niv.exe and b/exe/Niv.exe differ
diff --git a/prop/AssemblyInfo.cs b/prop/AssemblyInfo.cs
index e9267bc..7b5e110 100644
--- a/prop/AssemblyInfo.cs
+++ b/prop/AssemblyInfo.cs
@@ -20,5 +20,5 @@
ResourceDictionaryLocation.SourceAssembly
)]
-[assembly: AssemblyVersion("0.3.2.0")]
-[assembly: AssemblyFileVersion("0.3.2.0")]
+[assembly: AssemblyVersion("0.4.0.0")]
+[assembly: AssemblyFileVersion("0.4.0.0")]
diff --git a/res/theme-dark/icon-undelete.png b/res/theme-dark/icon-undelete.png
new file mode 100644
index 0000000..4c7bb01
Binary files /dev/null and b/res/theme-dark/icon-undelete.png differ
diff --git a/res/theme-light/icon-undelete.png b/res/theme-light/icon-undelete.png
new file mode 100644
index 0000000..6da34a9
Binary files /dev/null and b/res/theme-light/icon-undelete.png differ
diff --git a/src/Recycle.cs b/src/Recycle.cs
new file mode 100644
index 0000000..218f596
--- /dev/null
+++ b/src/Recycle.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.IO;
+using System.Text;
+using System.Windows;
+
+namespace Niv
+{
+ class Recycle
+ {
+ // Folder of recycle
+ private static string recyclePath = Environment.CurrentDirectory + @"\Recycle\";
+ private DirectoryInfo di = new DirectoryInfo(recyclePath);
+
+ // Id counter for every recycle files.
+ private int recycleId = 0;
+
+ // List of deleted files
+ private List recycleInfos = new List();
+
+ // Get the image count in recycle list.
+ public int count
+ {
+ get
+ {
+ return recycleInfos.Count;
+ }
+ }
+
+ public Recycle()
+ {
+ clean();
+ }
+
+ // Move a image here.
+ public void recieve(ImageInfo info, int index)
+ {
+ // Create the recycle folder if not exists.
+ di.Refresh();
+ if (!di.Exists) di.Create();
+
+ // Create the info
+ RecycleImageInfo recycleInfo = new RecycleImageInfo(info, recycleId, index);
+
+ // Move the file and rename it.
+ FileInfo fi = new FileInfo(info.filename);
+ string newName = recyclePath + recycleId + Path.GetExtension(info.filename);
+ recycleInfo.newFilename = newName;
+ fi.MoveTo(newName);
+
+ // Record the info
+ recycleInfos.Add(recycleInfo);
+
+ recycleId++;
+ }
+
+ // Move back the last deleted image.
+ public RecycleImageInfo undeleteLast()
+ {
+ return undelete(recycleInfos.Count - 1);
+ }
+
+ // Move a image out of here, return the original index.
+ public RecycleImageInfo undelete(int index)
+ {
+ RecycleImageInfo recycleInfo = recycleInfos[index];
+
+ // Move the file back.
+ FileInfo fi = new FileInfo(recycleInfo.newFilename);
+ fi.MoveTo(recycleInfo.originalInfo.filename);
+
+ recycleInfos.Remove(recycleInfo);
+
+ return recycleInfo;
+ }
+
+ // Delete all the files in recycle and the recycle folder.
+ public void clean()
+ {
+ if (di.Exists) di.Delete(true);
+ }
+
+ // EOC
+ }
+}
diff --git a/src/RecycleImageInfo.cs b/src/RecycleImageInfo.cs
new file mode 100644
index 0000000..3d0ef9d
--- /dev/null
+++ b/src/RecycleImageInfo.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Niv
+{
+ class RecycleImageInfo
+ {
+ // The original ImageInfo, used to undelete it.
+ public ImageInfo originalInfo;
+
+ // The id(filename) in recycle folder.
+ public int id;
+
+ // The original index in the image list. Used to insert it in the original index.
+ public int originalIndex;
+
+ // New filename
+ public string newFilename;
+
+ public RecycleImageInfo(ImageInfo info, int id, int originalIndex)
+ {
+ this.originalInfo = info;
+ this.id = id;
+ this.originalIndex = originalIndex;
+ }
+
+ // EOC
+ }
+}
diff --git a/src/Walker.cs b/src/Walker.cs
index cfc2b98..c8e3179 100644
--- a/src/Walker.cs
+++ b/src/Walker.cs
@@ -151,6 +151,12 @@ public void removeCurrentImageInfo()
if (currentIndex == imageInfos.Count) currentIndex = 0;
}
+ public void insertImageInfo(int index, ImageInfo info)
+ {
+ imageInfos.Insert(index, info);
+
+ }
+
// EOC
}
}
diff --git a/src/com.jarvisniu/I18n.cs b/src/com.jarvisniu/I18n.cs
index d04d97b..e6d3821 100644
--- a/src/com.jarvisniu/I18n.cs
+++ b/src/com.jarvisniu/I18n.cs
@@ -140,6 +140,10 @@ private static void loadLanguageData()
langData["zh-CN"]["tooltip.delete"] = "删除";
langData["zh-TW"]["tooltip.delete"] = "刪除";
+ langData["en-US"]["tooltip.undelete"] = "Undo Delete";
+ langData["zh-CN"]["tooltip.undelete"] = "撤销删除";
+ langData["zh-TW"]["tooltip.undelete"] = "撤銷刪除";
+
langData["en-US"]["tooltip.prev-image"] = "Previous";
langData["zh-CN"]["tooltip.prev-image"] = "上一张";
langData["zh-TW"]["tooltip.prev-image"] = "上一張";
diff --git a/xaml/NivWindow.xaml b/xaml/NivWindow.xaml
index 0aff07c..c02e40a 100644
--- a/xaml/NivWindow.xaml
+++ b/xaml/NivWindow.xaml
@@ -13,20 +13,23 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
diff --git a/xaml/NivWindow.xaml.cs b/xaml/NivWindow.xaml.cs
index 2822255..d0ee0dd 100644
--- a/xaml/NivWindow.xaml.cs
+++ b/xaml/NivWindow.xaml.cs
@@ -28,14 +28,14 @@ public partial class NivWindow : Window
Transformer transformer;
Controller inputController;
FolderWalker walker = new FolderWalker();
- // RecycleBin recycleBin = new RecycleBin(AppDomain.CurrentDomain.BaseDirectory);
+ Recycle recycle = new Recycle();
// delayed closing timers
Timer timerClosePage;
// layout config
- static double WINDOW_MIN_WIDTH = 680;
- static double WINDOW_MIN_HEIGHT = 462;
+ static double WINDOW_MIN_WIDTH = 540;
+ static double WINDOW_MIN_HEIGHT = 384;
public static int SEPARATOR_HEIGHT = 2;
public static int TOOLBAR_HEIGHT = 48;
public static int MARGIN_SIZE = 50;
@@ -51,7 +51,6 @@ public partial class NivWindow : Window
private Dictionary visibleStates = new Dictionary();
private bool isMarginBottomExist = true;
- private bool isSmoothButtonVisible = false;
private bool isZoomButtonInFitMode = true;
private bool isFullscreen = false;
private bool isAutoHideToolbar = false;
@@ -95,9 +94,10 @@ private void loadLanguage()
btnRotateLeft.ToolTip = I18n._("tooltip.rotate-left");
btnRotateRight.ToolTip = I18n._("tooltip.rotate-right");
btnDelete.ToolTip = I18n._("tooltip.delete");
+ btnUndelete.ToolTip = I18n._("tooltip.undelete");
btnPrevImage.ToolTip = I18n._("tooltip.prev-image");
btnNextImage.ToolTip = I18n._("tooltip.next-image");
- // Tips of smooth button and zoom button are dynamic in refreshSmoothButton() and refreshZoomButton().
+ // Tips of undelete, smooth and zoom button are dynamic in refreshFoobarButton().
btnMenu.ToolTip = I18n._("menu");
btnCloseInfo.ToolTip = I18n._("close");
btnExit.ToolTip = I18n._("tooltip.exit-program");
@@ -130,6 +130,7 @@ private void initLayout()
menu.Height = 0;
visibleStates[btnExit] = false;
+ visibleStates[btnSmooth] = false;
visibleStates[container] = false;
visibleStates[toolbar] = true;
visibleStates[info] = false;
@@ -138,14 +139,17 @@ private void initLayout()
// add animation effects to buttons
buttonAnimator.apply(btnZoom).apply(btnPrevImage).apply(btnNextImage).apply(btnSmooth).apply(btnMenu).apply(btnExit)
- .apply(btnDelete).apply(btnRotateLeft).apply(btnRotateRight).apply(menuAbout)
+ .apply(btnDelete).apply(btnUndelete).apply(btnRotateLeft).apply(btnRotateRight).apply(menuAbout)
.apply(menuHelp).apply(menuSetting).apply(menuImageInfo).apply(btnCloseInfo);
// Hide the toolbar buttons
onWalkerCountChanged();
+ // Hide undelete button
+ onRecycleCountChanged();
+
// Set render to high quality of images
- Image[] images = { imageRotateLeft, imageRotateRight, imageDelete, imagePrev, imageNext,
+ Image[] images = { imageRotateLeft, imageRotateRight, imageDelete, imageUndelete, imagePrev, imageNext,
imageSmooth, imageZoom, imageMenu, imageCloseInfo, imageHelp, imageAbout, imageSetting, imageInfo, imageExit };
foreach (Image image in images)
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
@@ -171,13 +175,14 @@ private void setButtonSmoothVisibility()
if (s > AA_SCALE_THRESHHOLD && walker.count > 0)
{
animatorJar.fadeIn(btnSmooth);
- isSmoothButtonVisible = true;
+ visibleStates[btnSmooth] = true;
}
else if (s <= AA_SCALE_THRESHHOLD)
{
animatorJar.fadeOut(btnSmooth);
- isSmoothButtonVisible = false;
+ visibleStates[btnSmooth] = false;
}
+ refreshSmoothButton();
}
private void setTheme()
@@ -225,6 +230,7 @@ private void setTheme()
imageRotateLeft.Source = loadThemeBitmap("icon-rotate-left.png");
imageRotateRight.Source = loadThemeBitmap("icon-rotate-right.png");
imageDelete.Source = loadThemeBitmap("icon-delete.png");
+ imageUndelete.Source = loadThemeBitmap("icon-undelete.png");
imagePrev.Source = loadThemeBitmap("icon-prev.png");
imageNext.Source = loadThemeBitmap("icon-next.png");
refreshSmoothButton();
@@ -263,7 +269,13 @@ private void bindToolbarButtonsEvents()
// Delete-image button click
btnDelete.MouseUp += (object sender, MouseButtonEventArgs e) =>
{
- MessageBox.Show("TODO: 图片删除功能正在开发");
+ delete();
+ };
+
+ // Delete-image button click
+ btnUndelete.MouseUp += (object sender, MouseButtonEventArgs e) =>
+ {
+ undeleteLast();
};
// Prev-image button click
@@ -281,7 +293,7 @@ private void bindToolbarButtonsEvents()
// Smooth switch button click
btnSmooth.MouseUp += (object sender, MouseButtonEventArgs e) =>
{
- if (isSmoothButtonVisible) setSmoothTo(!walker.currentImageInfo.smooth);
+ if (visibleStates[btnSmooth]) setSmoothTo(!walker.currentImageInfo.smooth);
refreshSmoothButton();
};
@@ -358,6 +370,7 @@ private void bindContainerEvents()
separator.SizeChanged += (object sender, SizeChangedEventArgs e) =>
{
refreshProgressI();
+ gridSwitch.Visibility = separator.ActualWidth < 480 ? Visibility.Hidden : Visibility.Visible;
};
container.MouseLeftButtonDown += (object sender, MouseButtonEventArgs e) =>
@@ -495,7 +508,7 @@ private void loadFromWalker()
// infos
labelInfoFilename.Content = Path.GetFileName(info.filename);
FileInfo fi = new FileInfo(info.filename);
- labelInfoSize.Content = humanifyNumber(fi.Length) + "B";
+ labelInfoSize.Content = suffixifyNumber(fi.Length) + "B";
labelInfoResolution.Content = transformer.bitmap.PixelWidth + " x " + transformer.bitmap.PixelHeight;
labelInfoDate.Content = dateTimeToString(fi.LastWriteTime);
this.Title = Path.GetFileName(info.filename) + " - " + I18n._("appName");
@@ -627,6 +640,9 @@ private void onWalkerCountChanged()
btnRotateLeft.Visibility = Visibility.Hidden;
btnRotateRight.Visibility = Visibility.Hidden;
btnZoom.Visibility = Visibility.Hidden;
+
+ image.Source = null;
+ ImageBehavior.SetAnimatedSource(image, null);
}
else
{
@@ -680,6 +696,7 @@ private string getFirstCommandLineFilename()
}
return "";
}
+
private void showMessage(string message)
{
MessageBox.Show(message);
@@ -718,10 +735,19 @@ private void setImageRotationBackToZero()
public void saveRotationToFile()
{
string filename = walker.currentImageInfo.filename;
+
double rotationAngle = walker.currentImageInfo.rotationAngle;
double savedAngle = walker.currentImageInfo.savedRotationAngle;
double angle = simplifyAngle(rotationAngle - savedAngle);
- if (angle == 0) return;
+ if (angle == 0)
+ {
+ return;
+ }
+ else if (Path.GetExtension(filename) == ".gif")
+ {
+ // showMessage("不支持保存GIF图片");
+ return;
+ }
System.Drawing.Image imgSrc = System.Drawing.Image.FromFile(filename);
if (angle == 90)
@@ -828,13 +854,14 @@ private void setSmoothByImageResolution()
bool isImageSmall = transformer.bitmap.PixelWidth < 257 && transformer.bitmap.PixelHeight < 257;
setSmoothTo(!isImageSmall);
}
-
+
private void refreshSmoothButton()
{
if (walker.currentImageInfo != null)
{
imageSmooth.Source = loadThemeBitmap(walker.currentImageInfo.smooth ? "icon-smooth-off.png" : "icon-smooth-on.png");
- btnSmooth.ToolTip = I18n._(walker.currentImageInfo.smooth ? "tooltip.disable-smooth" : "tooltip.enable-smooth");
+ btnSmooth.ToolTip = visibleStates[btnSmooth] ?
+ I18n._(walker.currentImageInfo.smooth ? "tooltip.disable-smooth" : "tooltip.enable-smooth") : null;
}
}
@@ -874,9 +901,9 @@ private double simplifyAngle(double angle)
return angle;
}
- private string humanifyNumber(double num)
+ // Convert a number into the form that has a suffix k, M, G etc.
+ private string suffixifyNumber(double num)
{
- // 科学计数法
string[] units = { "", "k", "M", "G", "T" };
int level = 0;
@@ -1101,7 +1128,7 @@ private void hideMainMenu()
animatorJar.heightTo(menu, 0);
animatorJar.fadeOut(menu);
}
-
+
#endregion
private void closeMenu(object sender, MouseButtonEventArgs e)
@@ -1127,34 +1154,54 @@ private void nextImage()
private void delete()
{
- if (walker.count == 0) return;
+ recycle.recieve(walker.currentImageInfo, walker.currentIndex);
+ walker.removeCurrentImageInfo();
+ // showMessage("图片已删除至图片回收站。");
- FileInfo fi = new FileInfo(walker.currentImageInfo.filename);
- if (fi.Exists)
- {
- //recycleBin.receive(filename);
- showMessage("图片已删除至图片回收站。");
- showPage();
- }
+ if (walker.count > 0) loadFromWalker();
- walker.removeCurrentImageInfo();
+ onWalkerCountChanged();
+ onRecycleCountChanged();
+ }
- if (walker.count > 0)
+ private void undeleteLast()
+ {
+ if (recycle.count > 0)
+ {
+ RecycleImageInfo recycleInfo = recycle.undeleteLast();
+ walker.insertImageInfo(recycleInfo.originalIndex, recycleInfo.originalInfo);
+ walker.currentIndex = recycleInfo.originalIndex;
loadFromWalker();
+
+ onWalkerCountChanged();
+ onRecycleCountChanged();
+ }
+ }
+
+ private void onRecycleCountChanged()
+ {
+ if (recycle.count > 0)
+ {
+ animatorJar.fadeIn(btnUndelete);
+ btnUndelete.ToolTip = I18n._("tooltip.undelete");
+ }
else
- exit(); // TODO不能退出,因为可能只有一张而删错,应该显示一个打开文件的按钮
+ {
+ animatorJar.fadeOut(btnUndelete);
+ btnUndelete.ToolTip = null;
+ }
}
// Press key "D" to show something for debugging.
private void debug()
{
- MessageBox.Show(progress.Margin.Bottom.ToString());
+ //MessageBox.Show(this.Height.ToString());
}
private void exit()
{
setImageRotationBackToZero();
- //recycleBin.clean();
+ recycle.clean();
aboutWindow.exit();
Application.Current.Shutdown();
}