Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed06924df1 | ||
|
|
b78179ccee | ||
|
|
5a00c5eb2a | ||
|
|
74fa41f151 | ||
|
|
32d5fc2c37 | ||
|
|
1049046d0b | ||
|
|
1f8e0a1e2b | ||
|
|
5d3d5251c7 |
No files matched your search
@@ -47,7 +47,7 @@ namespace mbts.ui
|
||||
//public static ImageSource Ticket { get { return load_image("Ticket.png"); } } // replaced with AddOrders
|
||||
public static ImageSource Date { get { return load_image("Date.png"); } }
|
||||
|
||||
public static ImageSource Cube3d { get { return (DrawingImage)_resources["Cube"]; /*return load_image("Cube-Molecule.png");*/ } }
|
||||
public static ImageSource Cube3d { get { return (DrawingImage)_resources["Cube"]; } }
|
||||
public static ImageSource Algo { get { return Cube3d; } }
|
||||
public static ImageSource Quote { get { return Math; } }
|
||||
public static ImageSource Math { get { return (DrawingImage)_resources["Surface"]; } }
|
||||
@@ -62,9 +62,7 @@ namespace mbts.ui
|
||||
#pragma warning restore CS0618
|
||||
|
||||
// Vector icons
|
||||
public static ImageSource Favorites { get { return Favorites_Gold; } }
|
||||
public static ImageSource Favorites_Gold { get { return (DrawingImage)_resources["FavoritesGold"]; } }
|
||||
public static ImageSource Favorites_White { get { return (DrawingImage)_resources["FavoritesWhite"]; } }
|
||||
public static ImageSource Bookmarks { get { return (DrawingImage)_resources["Bookmarks"]; } }
|
||||
public static ImageSource Window { get { return (DrawingImage)_resources["Window"]; } }
|
||||
public static ImageSource ActiveWindow { get { return (DrawingImage)_resources["ActiveWindow"]; } }
|
||||
public static ImageSource Trash { get { return (DrawingImage)_resources["Trash"]; } }
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -3,33 +3,17 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mbts="clr-namespace:mbts.ui">
|
||||
<DrawingImage x:Key="FavoritesGold">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing Geometry="M1540.22,2082.07L1546.95,2102.78 1568.73,2102.78 1551.11,2115.58 1557.84,2136.29 1540.22,2123.49 1522.6,2136.29 1529.33,2115.58 1511.71,2102.78 1533.49,2102.78 1540.22,2082.07z">
|
||||
<GeometryDrawing.Pen>
|
||||
<Pen Thickness="1" Brush="Black" />
|
||||
</GeometryDrawing.Pen>
|
||||
<GeometryDrawing.Brush>
|
||||
<SolidColorBrush Color="#ffd932" />
|
||||
</GeometryDrawing.Brush>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup.Children>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="FavoritesWhite">
|
||||
<DrawingImage x:Key="Bookmarks">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing Geometry="M1540.22,2082.07L1546.95,2102.78 1568.73,2102.78 1551.11,2115.58 1557.84,2136.29 1540.22,2123.49 1522.6,2136.29 1529.33,2115.58 1511.71,2102.78 1533.49,2102.78 1540.22,2082.07z">
|
||||
<GeometryDrawing.Pen>
|
||||
<Pen Thickness="1" Brush="Black" />
|
||||
<Pen Thickness="3" Brush="Black" />
|
||||
</GeometryDrawing.Pen>
|
||||
<GeometryDrawing.Brush>
|
||||
<SolidColorBrush Color="#ffd932" />
|
||||
<SolidColorBrush Color="Transparent" />
|
||||
</GeometryDrawing.Brush>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup.Children>
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
using bts.spark;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace bts.ui {
|
||||
[Export(typeof(IShellModule))]
|
||||
public class BookmarkShellModule : ILazyMenuModule {
|
||||
readonly WindowManager _wm;
|
||||
readonly IBookmarks _bookmarks;
|
||||
readonly ISavedWindowFactory _window_factory;
|
||||
readonly WorkspacesModel _workspaces;
|
||||
|
||||
[ImportingConstructor]
|
||||
public BookmarkShellModule(
|
||||
WorkspacesModel workspaces,
|
||||
WindowManager wm,
|
||||
IBookmarks bm,
|
||||
ISavedWindowFactory swf) {
|
||||
_workspaces = workspaces;
|
||||
_wm = wm;
|
||||
_bookmarks = bm;
|
||||
_window_factory = swf;
|
||||
}
|
||||
|
||||
public ImageSource icon => Icons.Bookmarks;
|
||||
|
||||
public string name => "My Windows";
|
||||
|
||||
public int priority => 35;
|
||||
|
||||
public IEnumerable<SparkCommand> actions {
|
||||
get {
|
||||
// The windows that are open
|
||||
var open =
|
||||
_wm.list_documents()
|
||||
.Select(td => td.ViewModel)
|
||||
.Where(vm => vm.HasCustomTitle)
|
||||
.Select(vm =>
|
||||
SparkCommand.create(vm.Title, vm.Icon, SparkTags.ActivateWindow, () => vm.Model))
|
||||
.Cast<SparkCommand>();
|
||||
|
||||
var open_set = new HashSet<string>(open.Select(c => c.title));
|
||||
|
||||
// The windows that are not open
|
||||
var closed =
|
||||
_bookmarks.bookmarks
|
||||
.Where(state => !open_set.Contains(state.title))
|
||||
.Select(state =>
|
||||
SparkCommand.create(state.title, Icons.Bookmarks, SparkTags.ShowWindowAsync,
|
||||
() => {
|
||||
_bookmarks.remove(state);
|
||||
return _window_factory.restore(state);
|
||||
}))
|
||||
.Cast<SparkCommand>();
|
||||
|
||||
// todo: The windows that are closed
|
||||
foreach (var w in open.Concat(closed).OrderBy(w => w.title)) {
|
||||
yield return w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IShellWindow> create(Stream load_stream) {
|
||||
// This module shouldn't have any windows
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using mbts.ui.settings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.ui.shell {
|
||||
|
||||
public interface IBookmarks {
|
||||
void save_bookmark(IShellWindow window);
|
||||
void save_bookmark(WindowState window);
|
||||
void remove(WindowState window);
|
||||
void clear();
|
||||
IEnumerable<WindowState> bookmarks { get; }
|
||||
}
|
||||
|
||||
[Export(typeof(IBookmarks))]
|
||||
public class BookmarksModel : IBookmarks {
|
||||
readonly ISettingsCollector _settings_collector;
|
||||
readonly Dictionary<string, WindowState> _bookmarks = new Dictionary<string, WindowState>();
|
||||
|
||||
[ImportingConstructor]
|
||||
public BookmarksModel(ISettingsCollector settings) {
|
||||
_settings_collector = settings;
|
||||
}
|
||||
public void save_bookmark(IShellWindow window) {
|
||||
var state = _settings_collector.get_window_state(window);
|
||||
if (state != null) {
|
||||
_bookmarks[state.title] = state;
|
||||
}
|
||||
}
|
||||
public void save_bookmark(WindowState window) {
|
||||
_bookmarks[window.title] = window;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
_bookmarks.Clear();
|
||||
}
|
||||
|
||||
public void remove(WindowState window) {
|
||||
_bookmarks.Remove(window.title);
|
||||
}
|
||||
|
||||
public IEnumerable<WindowState> bookmarks => _bookmarks.Values;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ namespace bts.ui {
|
||||
|
||||
public int priority => 40;
|
||||
|
||||
public IEnumerable<SparkCommand> actions => new []{
|
||||
public IEnumerable<SparkCommand> actions => new SparkCommand[]{
|
||||
SparkCommands.ShowWorkspaces
|
||||
//SparkCommands.ShowSettings
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace mbts.ui.tests
|
||||
Logger.log(bts.log.Level.Info, bts.log.Subsystem.Ui, "Begin: test_windows_disposed");
|
||||
// Make sure that an IShellWindow and ISlowWindow added to the Windows collection is eventually disposed
|
||||
var thread_factory = new ShellThreadFactory();
|
||||
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
|
||||
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), null, new SimpleBus());
|
||||
Assert.IsNotNull(Application.Current);
|
||||
Assert.IsNotNull(Application.Current.Windows);
|
||||
Assert.AreEqual(Application.Current.Windows.Count, 0);
|
||||
@@ -100,7 +100,7 @@ namespace mbts.ui.tests
|
||||
Logger.log(bts.log.Level.Info, bts.log.Subsystem.Ui, "Begin: test_slow_windows_disposed");
|
||||
// Make sure that an IShellWindow and ISlowWindow added to the Windows collection is eventually disposed
|
||||
var thread_factory = new ShellThreadFactory();
|
||||
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
|
||||
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), null, new SimpleBus());
|
||||
Assert.AreEqual(Application.Current.Windows.Count, 0);
|
||||
|
||||
// an ISlowWindow gets its own thread
|
||||
|
||||
@@ -5,6 +5,7 @@ using bts.spark;
|
||||
using mbts.ui;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using static mbts.spark.SparkBusData;
|
||||
|
||||
namespace mbts.spark
|
||||
@@ -34,6 +35,8 @@ namespace mbts.spark
|
||||
public static ITag<(PartialCommand<SecurityMD>, SecurityCriteria)> FilterPickOne { get; } = new SparkTag<(PartialCommand<SecurityMD>, SecurityCriteria)>("FilterPick1");
|
||||
|
||||
public static ITag<IShellWindow> ShowWindow { get; } = new SparkTag<IShellWindow>("Show Window");
|
||||
public static ITag<Task<IShellWindow>> ShowWindowAsync { get; } = new SparkTag<Task<IShellWindow>>("ShowWindowAsync");
|
||||
public static ITag<IShellWindow> ActivateWindow { get; } = new SparkTag<IShellWindow>("Activate Window");
|
||||
public static ITag<Nothing> ShowWorkspaces { get; } = new SparkTag<Nothing>(nameof(ShowWorkspaces));
|
||||
public static ITag<Nothing> ShowSettings { get; } = new SparkTag<Nothing>(nameof(ShowSettings));
|
||||
public static ITag<Uri> ShowUri { get; } = new SparkTag<Uri>("ShowUri");
|
||||
|
||||
@@ -48,10 +48,10 @@ namespace System.Collections.Generic {
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<T> ToIEnumerable<T>(this T item) {
|
||||
yield return item;
|
||||
}
|
||||
// use "new [] { item }" - avoid putting this on the intellisense for every item everywhere
|
||||
//public static IEnumerable<T> ToIEnumerable<T>(this T item) {
|
||||
// yield return item;
|
||||
//}
|
||||
|
||||
public static IEnumerable<T> TakeWhile<T, TAccumulate>(this IEnumerable<T> source, TAccumulate seed, Func<TAccumulate, T, TAccumulate> accumulate, Func<TAccumulate, T, bool> predicate) {
|
||||
TAccumulate a = seed;
|
||||
|
||||
@@ -69,16 +69,18 @@ namespace mbts.ui.settings
|
||||
if (window == null || window.Owner == null) return null;
|
||||
MemoryStream out_stream = new MemoryStream();
|
||||
window.GetState(out_stream);
|
||||
|
||||
var pos = _interop.GetPosition(window);
|
||||
var state = new WindowState() {
|
||||
owner = window.Owner.key(),
|
||||
app_data = out_stream.ToArray(),
|
||||
position = _interop.GetPosition(window),
|
||||
title = window.Title
|
||||
position = pos,
|
||||
title = string.IsNullOrEmpty(pos.custom_title) ? window.Title : pos.custom_title
|
||||
};
|
||||
return state;
|
||||
}
|
||||
catch (Exception e) {
|
||||
Logger.log(bts.log.Level.Error, bts.log.Subsystem.Ui, "Error saving window state for type {0}.\n {1}".fmt(window.GetType(), e.Message));
|
||||
Logger.log(Level.Error, Subsystem.Ui, "Error saving window state for type {0}.\n {1}".fmt(window.GetType(), e.Message));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,9 @@ namespace mbts.ui.shell {
|
||||
[ProtoMember(102), DefaultValue(true)]
|
||||
public bool always_on_top = true;
|
||||
//[ProtoMember(103)] //used
|
||||
|
||||
[ProtoMember(104)]
|
||||
public string custom_title;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
@@ -58,7 +61,7 @@ namespace mbts.ui.shell {
|
||||
public WindowPosition position;
|
||||
|
||||
[ProtoMember(9)]
|
||||
public string title;
|
||||
public string title; // for debugging
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
|
||||
@@ -23,6 +23,9 @@ namespace mbts.ui
|
||||
Task<IShellWindow> create(Stream load_stream);
|
||||
}
|
||||
|
||||
// ILazyModule may also impement IShellMenu to get opening/closing notification
|
||||
public interface ILazyMenuModule : IShellModule { }
|
||||
|
||||
public interface IPersistShellModule : IShellModule {
|
||||
// Called at startup before the UI is shown
|
||||
// This is an opportunity for you to kick off background services
|
||||
|
||||
@@ -17,5 +17,5 @@ namespace mbts.ui
|
||||
WindowPosition GetPosition(IShellWindow window);
|
||||
bool IsVisible(IShellWindow window);
|
||||
void PinWindow(IShellWindow window);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,11 @@ namespace mbts.ui.controls {
|
||||
static SparkMenu() =>
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(SparkMenu), new FrameworkPropertyMetadata(typeof(SparkMenu)));
|
||||
|
||||
public SparkMenu() =>
|
||||
public SparkMenu() {
|
||||
SetCurrentValue(FocusableProperty, false);
|
||||
}
|
||||
|
||||
|
||||
public SparkMenu(IEnumerable<SparkCommand> commands, IBus bus) : this() =>
|
||||
ItemsSource = commands.Select(c => new SparkCommandVM(c, bus)).ToList();
|
||||
|
||||
|
||||
@@ -61,16 +61,16 @@ namespace mbts.ui
|
||||
_source_window = window as FloatingWindow;
|
||||
if (_source != null) {
|
||||
_mouse_start = e.GetPosition(window);
|
||||
if (!is_button_descendant((DependencyObject)e.OriginalSource)) {
|
||||
if (!is_clickable_descendant((DependencyObject)e.OriginalSource)) {
|
||||
Mouse.Capture(_source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_button_descendant(DependencyObject target) {
|
||||
static bool is_clickable_descendant(DependencyObject target) {
|
||||
while (target != null && !(target is TabDocument)) {
|
||||
target = VisualTreeHelper.GetParent(target);
|
||||
if (target is Button) return true;
|
||||
if (target is Button || target is TextBox) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -24,38 +24,7 @@ namespace mbts.ui
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task FromEvent(this object target, string event_name) {
|
||||
var evt = target.GetType().GetEvent(event_name);
|
||||
var delegate_type = evt.EventHandlerType;
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
Action result = () => { tcs.TrySetResult(null); };
|
||||
var d = Delegate.CreateDelegate(evt.EventHandlerType, result, "Invoke");
|
||||
try {
|
||||
evt.AddEventHandler(target, d);
|
||||
await tcs.Task;
|
||||
}
|
||||
finally {
|
||||
evt.RemoveEventHandler(target, d);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<T> FromEvent<T>(this object target, string event_name) {
|
||||
var evt = target.GetType().GetEvent(event_name);
|
||||
var delegate_type = evt.EventHandlerType;
|
||||
var tcs = new TaskCompletionSource<T>();
|
||||
Action<T> result = (arg) => { tcs.TrySetResult(arg); };
|
||||
var d = Delegate.CreateDelegate(evt.EventHandlerType, result, "Invoke");
|
||||
try {
|
||||
evt.AddEventHandler(target, d);
|
||||
await tcs.Task;
|
||||
return tcs.Task.Result;
|
||||
}
|
||||
finally {
|
||||
evt.RemoveEventHandler(target, d);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Tuple<T1,T2>> FromEvent<T1,T2>(this object target, string event_name) {
|
||||
public static async Task<Tuple<T1,T2>> FromEvent<T1,T2>(this DependencyObject target, string event_name) {
|
||||
var evt = target.GetType().GetEvent(event_name);
|
||||
var delegate_type = evt.EventHandlerType;
|
||||
var tcs = new TaskCompletionSource<Tuple<T1,T2>>();
|
||||
|
||||
@@ -14,18 +14,19 @@ namespace mbts.ui.shell
|
||||
public partial class FloatingWindow : Window
|
||||
{
|
||||
bool _closed = false;
|
||||
private IUndoActions _undo;
|
||||
readonly IBookmarks _bookmark;
|
||||
private IWindowManager _wm;
|
||||
private ISavedWindowFactory _window_factory;
|
||||
private ISettingsCollector _window_settings;
|
||||
private Stack<Action> _recently_closed_windows = new Stack<Action>();
|
||||
|
||||
// if is_loaded is true, will NOT set width/height
|
||||
internal FloatingWindow(TabDocument document, IWindowManager wm, IUndoActions undo, ISettingsCollector settings, ISavedWindowFactory factory, bool is_loaded=false) {
|
||||
internal FloatingWindow(TabDocument document,
|
||||
IWindowManager wm, IBookmarks bookmarks, ISettingsCollector settings, ISavedWindowFactory factory, bool is_loaded=false) {
|
||||
_wm = wm;
|
||||
_undo = undo;
|
||||
_window_settings = settings;
|
||||
_window_factory = factory;
|
||||
_bookmark = bookmarks;
|
||||
|
||||
InitializeComponent();
|
||||
if (string.IsNullOrEmpty(document.ViewModel.host_id)) {
|
||||
@@ -67,6 +68,7 @@ namespace mbts.ui.shell
|
||||
|
||||
|
||||
private ItemCollection Items { get { return _tabs.Items; } }
|
||||
public IEnumerable<TabDocument> Documents => _tabs.Items.OfType<TabDocument>();
|
||||
public bool IsEmpty { get { return _tabs.Items.Count == 0; } }
|
||||
public bool SingleChild { get { return _tabs.Items.Count == 1; } }
|
||||
public string host_id { get; private set; }
|
||||
@@ -88,10 +90,10 @@ namespace mbts.ui.shell
|
||||
if (sender != this) {
|
||||
var undoaction = create_undo_action(document);
|
||||
if (undoaction != null) {
|
||||
IDisposable undoitem = _undo.push("Restore Tab '{0}'".fmt(document.Title), undoaction);
|
||||
_recently_closed_windows.Push(() => { undoaction(); undoitem.Dispose(); });
|
||||
_recently_closed_windows.Push(() => { undoaction(); });
|
||||
}
|
||||
}
|
||||
_bookmark.save_bookmark(document.Model);
|
||||
}
|
||||
|
||||
public void remove_document(TabDocument document) {
|
||||
@@ -144,22 +146,9 @@ namespace mbts.ui.shell
|
||||
|
||||
protected override void OnClosed(EventArgs e) {
|
||||
_closed = true;
|
||||
TabDocument[] items = _tabs.Items.OfType<TabDocument>().ToArray();
|
||||
if (items.Length > 0) {
|
||||
// If we're closing and still have Tabs open we save the whole "window" worth of state in the undo stack
|
||||
// If the application is shutting down this might be unnecessary work but it's not too costly
|
||||
var undo_actions = items.Select(t => create_undo_action(t.ViewModel)).Where(item => item != null).ToArray();
|
||||
if (undo_actions.Length > 0) {
|
||||
string name = "Restore Window: '{0}'".fmt(string.Join("', '", items.Select(t => t.ViewModel.Title)));
|
||||
_undo.push(name, () => {
|
||||
foreach (var u in undo_actions) { u(); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
base.OnClosed(e);
|
||||
|
||||
foreach (TabDocument document in items) {
|
||||
foreach (TabDocument document in _tabs.Items.OfType<TabDocument>().ToList()) {
|
||||
if (document != null && document.ViewModel.CloseCommand.CanExecute(this)) {
|
||||
document.ViewModel.CloseCommand.Execute(this);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d" Style="{StaticResource {x:Type TabItem}}" d:DesignWidth="125"
|
||||
xmlns:ui="clr-namespace:mbts.ui"
|
||||
xmlns:convert="clr-namespace:mbts.ui.converters"
|
||||
mc:Ignorable="d" d:DesignWidth="125"
|
||||
Header="{Binding}" Content="{Binding AppContent}"
|
||||
Background="#e6e6e6">
|
||||
|
||||
@@ -18,6 +20,18 @@
|
||||
<Storyboard x:Key="_slide_back">
|
||||
<DoubleAnimation Storyboard.TargetName="translate_transform" Storyboard.TargetProperty="X" To="100" Duration="0:0:0.25" FillBehavior="Stop" />
|
||||
</Storyboard>
|
||||
<DataTemplate x:Key="_edit_template">
|
||||
<TextBox Text="{Binding TitleOverride}"
|
||||
ui:TextBoxBehavior.CommitOnEnter="True"
|
||||
ui:TextBoxBehavior.FocusOnLoaded="True"
|
||||
ui:TextBoxBehavior.SelectAllOnFocus="True"
|
||||
ui:TextBoxBehavior.ReleaseOnEscape="True"
|
||||
Width="150" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="0,0,4,0" />
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="_template">
|
||||
<TextBlock Text="{Binding Title}" TextTrimming="CharacterEllipsis" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="0,0,4,0"></TextBlock>
|
||||
</DataTemplate>
|
||||
<convert:BooleanToObjectConverter x:Key="MenuHeaderConverter" ValueForTrue="Rename Window" ValueForFalse="Add to My Windows" />
|
||||
</ResourceDictionary>
|
||||
</TabItem.Resources>
|
||||
<TabItem.LayoutTransform>
|
||||
@@ -28,25 +42,32 @@
|
||||
<TabItem.RenderTransform>
|
||||
<TranslateTransform x:Name="translate_transform" X="0" />
|
||||
</TabItem.RenderTransform>
|
||||
<TabItem.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Title}" TextTrimming="CharacterEllipsis" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="0,0,4,0"> </TextBlock>
|
||||
</DataTemplate>
|
||||
</TabItem.HeaderTemplate>
|
||||
|
||||
<TabItem.Template>
|
||||
<ControlTemplate TargetType="{x:Type TabItem}">
|
||||
<Border x:Name="bd" BorderBrush="Black" BorderThickness="0,0,0.5,0" Background="#d6d6d6" Width="{DynamicResource TabWidth}" Height="{DynamicResource TabHeight}" MinWidth="40" Margin="0" SnapsToDevicePixels="True" DockPanel.Dock="Left">
|
||||
<Border.ToolTip>
|
||||
<TextBlock Text="{Binding Title}" />
|
||||
</Border.ToolTip>
|
||||
<Border.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{Binding HasCustomTitle, Converter={StaticResource MenuHeaderConverter}}" Command="{Binding rename_command}">
|
||||
<MenuItem.Icon>
|
||||
<Image Source="{StaticResource Bookmarks}" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Remove from My Windows" Command="{Binding unrename_command}" IsEnabled="{Binding HasCustomTitle}" />
|
||||
</ContextMenu>
|
||||
</Border.ContextMenu>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image SnapsToDevicePixels="True" MaxHeight="20" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Uniform" Margin="5,0,5,0" Source="{Binding Icon, Mode=OneTime}" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<Image SnapsToDevicePixels="True" MaxHeight="20" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Uniform" Margin="5,0,5,0"
|
||||
Source="{Binding Icon, Mode=OneTime}" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<!--<Image MaxHeight="10" HorizontalAlignment="Left" VerticalAlignment="Bottom" Stretch="Uniform" Margin="5"
|
||||
Source="{StaticResource Bookmarks}" Visibility="{Binding HasCustomTitle, Converter={StaticResource BoolToVisibility}}" />-->
|
||||
<ContentPresenter Grid.Column="1" x:Name="content" ContentSource="Header" TextBlock.Foreground="#666"
|
||||
RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
|
||||
HorizontalAlignment="Left"
|
||||
@@ -62,12 +83,18 @@
|
||||
<Setter Property="Panel.ZIndex" Value="1" />
|
||||
<Setter Property="Background" TargetName="bd" Value="#F9F9F9" />
|
||||
<Setter Property="TextBlock.Foreground" TargetName="content" Value="black" />
|
||||
<!--<Setter Property="BorderThickness" TargetName="bd" Value="0,4,0.5,0" />-->
|
||||
<!--<Setter Property="Margin" Value="-2,-2,-2,-1" />
|
||||
<Setter Property="Margin" TargetName="content" Value="0,0,0,1" />
|
||||
<Setter Property="Margin" TargetName="bd" Value="0,0,0,0" />-->
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</TabItem.Template>
|
||||
<TabItem.Style>
|
||||
<Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
|
||||
<Setter Property="HeaderTemplate" Value="{StaticResource _template}" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding EditMode}" Value="True">
|
||||
<Setter Property="HeaderTemplate" Value="{StaticResource _edit_template}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TabItem.Style>
|
||||
</TabItem>
|
||||
@@ -18,8 +18,15 @@ namespace mbts.ui.shell {
|
||||
public ImageSource IconSource { get { return _module.icon; } }
|
||||
public UIElement MenuContent => new SparkMenu(_module.actions, _bus);
|
||||
|
||||
public void opening() { }
|
||||
public void opening() {
|
||||
(_module as IShellMenu)?.opening();
|
||||
if (_module is ILazyMenuModule) {
|
||||
RaisePropertyChanged(nameof(MenuContent));
|
||||
}
|
||||
}
|
||||
|
||||
public void closing() { }
|
||||
public void closing() {
|
||||
(_module as IShellMenu)?.closing();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,10 +24,11 @@ namespace mbts.ui.shell
|
||||
Dispatcher _dispatcher;
|
||||
HwndContentHost _hwnd_host;
|
||||
|
||||
|
||||
public ShellWindowVM(IShellWindow model, IWindowManager wm, IShellThreadFactory threads, string host = "") {
|
||||
Logger.assert(model != null);
|
||||
_model = model;
|
||||
_model.TitleChanged += () => RaisePropertyChanged("Title");
|
||||
_model.TitleChanged += () => { if (!HasCustomTitle) RaisePropertyChanged("Title"); };
|
||||
_model.IconChanged += () => { _icon = null; RaisePropertyChanged("Icon"); };
|
||||
_wm = wm;
|
||||
host_id = host;
|
||||
@@ -40,13 +41,55 @@ namespace mbts.ui.shell
|
||||
if (HasTabAffinity) {
|
||||
this.host_id = ((ITabAffineWindow)model).tab_group;
|
||||
}
|
||||
|
||||
_edit_mode = backing(nameof(EditMode), false);
|
||||
}
|
||||
|
||||
public string host_id { get; set; }
|
||||
public Dispatcher Dispatcher { get { return _dispatcher; } }
|
||||
public string Title { get { return _model.Title; } }
|
||||
|
||||
public string Title { get { return HasCustomTitle? TitleOverride : _model.Title; } }
|
||||
|
||||
string _title_override;
|
||||
public string TitleOverride {
|
||||
get => _title_override;
|
||||
set {
|
||||
if (_title_override != value) {
|
||||
if (value == _model.Title) {
|
||||
_title_override = null;
|
||||
}
|
||||
else {
|
||||
_title_override = value;
|
||||
}
|
||||
RaisePropertyChanged(nameof(TitleOverride), nameof(Title), nameof(HasCustomTitle));
|
||||
}
|
||||
_edit_mode.set(false);
|
||||
}
|
||||
}
|
||||
public bool HasCustomTitle => !string.IsNullOrWhiteSpace(TitleOverride);
|
||||
|
||||
Property<bool> _edit_mode;
|
||||
public bool EditMode => _edit_mode.Value;
|
||||
|
||||
|
||||
public ICommand rename_command => new DelegateCommand(begin_rename);
|
||||
public ICommand unrename_command => new DelegateCommand(remove_custom);
|
||||
|
||||
private void begin_rename() {
|
||||
if (string.IsNullOrEmpty(TitleOverride)) {
|
||||
_title_override = Title;
|
||||
}
|
||||
_edit_mode.set(true);
|
||||
}
|
||||
|
||||
private void remove_custom() {
|
||||
TitleOverride = string.Empty;
|
||||
//begin_close(this);
|
||||
}
|
||||
|
||||
public string OwnerName { get { return _model.Owner == null ? "" : _model.Owner.name; } }
|
||||
public ICommand CloseCommand { get { return new DelegateCommand(begin_close); } }
|
||||
|
||||
public double InitialWidth { get; private set; }
|
||||
public double InitialHeight { get; private set; }
|
||||
|
||||
@@ -155,18 +198,14 @@ namespace mbts.ui.shell
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetSaveState() {
|
||||
MemoryStream stream = new MemoryStream();
|
||||
_model.GetState(stream);
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
public IShellWindow Model { get { return _model; } }
|
||||
|
||||
public bool IsSlow { get { return _model is ISlowWindow; } }
|
||||
public bool IsShared { get { return _model is ISharedWindow; } }
|
||||
public bool HasTabAffinity { get { return _model is ITabAffineWindow; } }
|
||||
|
||||
|
||||
|
||||
public void Dispose() {
|
||||
if (IsShared) {
|
||||
// Shared threads continue running
|
||||
|
||||
@@ -20,25 +20,31 @@ namespace mbts.ui.shell {
|
||||
|
||||
[Export(typeof(IWindowManager))]
|
||||
[Export(typeof(IWindowsInterop))]
|
||||
[Export]
|
||||
public class WindowManager : IWindowManager, IWindowsInterop, IDisposable {
|
||||
Dictionary<IShellWindow, TabDocument> _documents = new Dictionary<IShellWindow, TabDocument>();
|
||||
Dictionary<string, FloatingWindow> _windows = new Dictionary<string, FloatingWindow>();
|
||||
IShellThreadFactory _threads;
|
||||
readonly IShellThreadFactory _threads;
|
||||
Lazy<ISettingsCollector> _window_settings;
|
||||
ISavedWindowFactory _window_factory;
|
||||
IUndoActions _undo;
|
||||
readonly ISavedWindowFactory _window_factory;
|
||||
readonly Lazy<IBookmarks> _bookmarks;
|
||||
IDisposable _dispose;
|
||||
object _lock = new object();
|
||||
|
||||
[ImportingConstructor]
|
||||
public WindowManager(
|
||||
IShellThreadFactory threads, Lazy<ISettingsCollector> settings,
|
||||
ISavedWindowFactory factory, IUndoActions undo, IBus bus) {
|
||||
ISavedWindowFactory factory, Lazy<IBookmarks> bookmarks, IBus bus) {
|
||||
_threads = threads;
|
||||
_window_settings = settings;
|
||||
_window_factory = factory;
|
||||
_undo = undo;
|
||||
_dispose = bus.subscribe(SparkTags.ShowWindow).OnDispatcher().Subscribe(w => add(w).ignore());
|
||||
_bookmarks = bookmarks;
|
||||
_dispose =
|
||||
Disposable.Create(
|
||||
bus.subscribe(SparkTags.ShowWindow).OnDispatcher().Subscribe(w => add(w).ignore()),
|
||||
bus.subscribe(SparkTags.ShowWindowAsync).OnDispatcher().Subscribe(w => add(w).ignore()),
|
||||
bus.subscribe(SparkTags.ActivateWindow).OnDispatcher().Subscribe(MakeVisible)
|
||||
);
|
||||
}
|
||||
|
||||
#region Window creation
|
||||
@@ -48,6 +54,7 @@ namespace mbts.ui.shell {
|
||||
var state = model.start_position();
|
||||
if (state != null) {
|
||||
viewmodel.host_id = state.host_id ?? "";
|
||||
viewmodel.TitleOverride = state.custom_title;
|
||||
}
|
||||
var window = CreateWindow(content, state);
|
||||
|
||||
@@ -66,10 +73,10 @@ namespace mbts.ui.shell {
|
||||
}
|
||||
} else {
|
||||
if (position != null) {
|
||||
window = new FloatingWindow(document, this, _undo, _window_settings.Value, _window_factory, true);
|
||||
window = new FloatingWindow(document, this, _bookmarks.Value, _window_settings.Value, _window_factory, true);
|
||||
window.set_position(position);
|
||||
} else
|
||||
window = new FloatingWindow(document, this, _undo, _window_settings.Value, _window_factory);
|
||||
window = new FloatingWindow(document, this, _bookmarks.Value, _window_settings.Value, _window_factory);
|
||||
_windows.Add(window.host_id, window);
|
||||
window.Closed += (o, e) => _windows.Remove(window.host_id);
|
||||
}
|
||||
@@ -81,12 +88,19 @@ namespace mbts.ui.shell {
|
||||
public IReadOnlyList<IShellWindow> list_windows() =>
|
||||
_documents.Keys.ToList();
|
||||
|
||||
public IReadOnlyList<TabDocument> list_documents() => _documents.Values.ToList();
|
||||
|
||||
public async Task add(IShellWindow window) {
|
||||
if (!Application.Current.Dispatcher.CheckAccess())
|
||||
await Application.Current.Dispatcher.InvokeAsync(() => add(window));
|
||||
Logger.info(bts.log.Subsystem.Ui, $"Opening window {window.diagnostic_name()}.");
|
||||
lock (_lock)
|
||||
lock (_lock) {
|
||||
_documents[window] = _create_document(window);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task add(Task<IShellWindow> window) {
|
||||
await add(await window);
|
||||
}
|
||||
|
||||
public void remove(IShellWindow window) {
|
||||
@@ -139,14 +153,13 @@ namespace mbts.ui.shell {
|
||||
|
||||
#region IWindowsInterop
|
||||
public WindowPosition GetPosition(IShellWindow shell_window) {
|
||||
return GetPosition(shell_window, null);
|
||||
}
|
||||
public WindowPosition GetPosition(IShellWindow shell_window, FloatingWindow owning_window) {
|
||||
FloatingWindow window = owning_window == null ? window_for(shell_window) : owning_window;
|
||||
FloatingWindow window = window_for(shell_window);
|
||||
var doc = window.Documents.FirstOrDefault(td => td.ViewModel.Model == shell_window);
|
||||
if (window != null) {
|
||||
WindowPosition pos = window.get_position();
|
||||
pos.tab_index = window.document_index(shell_window);
|
||||
pos.host_id = window.host_id;
|
||||
pos.custom_title = doc?.ViewModel?.TitleOverride;
|
||||
return pos;
|
||||
}
|
||||
Logger.log(bts.log.Level.Warn, bts.log.Subsystem.Ui, "IShellWindow '{0}' does not have a valid position. It may not be displayed".fmt(shell_window.Title));
|
||||
|
||||
@@ -19,6 +19,7 @@ using mbts.persist;
|
||||
|
||||
namespace mbts.ui.shell
|
||||
{
|
||||
|
||||
[Export]
|
||||
public class WorkspacesModel : ViewModelBaseWithValidation
|
||||
{
|
||||
@@ -35,10 +36,11 @@ namespace mbts.ui.shell
|
||||
private bool _closing = false;
|
||||
private ILauncher _launcher;
|
||||
private IUndoActions _undo;
|
||||
readonly IBookmarks _bookmarks;
|
||||
|
||||
[ImportingConstructor]
|
||||
public WorkspacesModel(ISettingsCollector settings, ISavedWindowFactory window_restore,
|
||||
IWindowManager windows, ObservableCollection<IShellModule> modules, IPersist persist,
|
||||
IWindowManager windows, ObservableCollection<IShellModule> modules, IPersist persist, IBookmarks bm,
|
||||
IUserPersist user_persist, ILauncher launcher, IUndoActions undo, [Import("workspace")]string initial_workspace) {
|
||||
_settings_collector = settings;
|
||||
_window_restore = window_restore;
|
||||
@@ -48,6 +50,7 @@ namespace mbts.ui.shell
|
||||
_modules = modules;
|
||||
_launcher = launcher;
|
||||
_undo = undo;
|
||||
_bookmarks = bm;
|
||||
_launcher.closing += _launcher_closing;
|
||||
Workspaces = new ObservableCollection<WorkspaceViewModel>();
|
||||
init(initial_workspace).ignore();
|
||||
@@ -150,6 +153,7 @@ namespace mbts.ui.shell
|
||||
workspace.windows = _settings_collector.get_window_state().ToList();
|
||||
workspace.modules = _settings_collector.get_module_state().ToList();
|
||||
workspace.launcher_state = _settings_collector.get_launcher_state();
|
||||
workspace.named_windows = _bookmarks.bookmarks.ToList();
|
||||
return workspace;
|
||||
}
|
||||
|
||||
@@ -166,6 +170,7 @@ namespace mbts.ui.shell
|
||||
await Task.Yield();
|
||||
}
|
||||
workspace.launcher_state = _settings_collector.get_launcher_state();
|
||||
workspace.named_windows = _bookmarks.bookmarks.ToList();
|
||||
return workspace;
|
||||
}
|
||||
|
||||
@@ -226,10 +231,15 @@ namespace mbts.ui.shell
|
||||
}
|
||||
}
|
||||
|
||||
// Notify workspace changed
|
||||
if (WorkspaceLoaded != null) {
|
||||
WorkspaceLoaded(name);
|
||||
_bookmarks.clear();
|
||||
if (def.named_windows != null) {
|
||||
foreach (var window in def.named_windows) {
|
||||
_bookmarks.save_bookmark(window);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify workspace changed
|
||||
WorkspaceLoaded?.Invoke(name);
|
||||
}
|
||||
|
||||
public Task delete_workspace(string name, WorkspaceDefinition def) {
|
||||
@@ -244,18 +254,20 @@ namespace mbts.ui.shell
|
||||
}
|
||||
return (def.is_shared ? _public : _private).clear(WORKSPACE_NAMESPACE, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The serialized data for the Workspace
|
||||
[ProtoContract(Name = "WorkspaceDefinition")]
|
||||
public class WorkspaceDefinition
|
||||
{
|
||||
// Converted the protobuf type ShellSettings to c#
|
||||
// Avoid re-using id: 1,3,4,5,6,7
|
||||
|
||||
[ProtoMember(2, IsRequired = false)]
|
||||
public List<WindowState> windows { get; set; }
|
||||
|
||||
// named windows which are not currently open
|
||||
[ProtoMember(3)]
|
||||
public List<WindowState> named_windows { get; set; }
|
||||
|
||||
[ProtoMember(9, IsRequired = false)]
|
||||
public List<ModuleState> modules { get; set; }
|
||||
|
||||
|
||||
@@ -37,13 +37,13 @@ namespace bts.spark
|
||||
if (this.IsRoot) {
|
||||
return Enumerable.Empty<OrderNode>();
|
||||
}
|
||||
return this.Parent.ToIEnumerable().Concat(this.Parent.Ancestors);
|
||||
return new[] { this.Parent }.Concat(this.Parent.Ancestors);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<OrderNode> MeAndDescedants {
|
||||
get {
|
||||
return this.ToIEnumerable().Concat(this.children.SelectMany(c => c.MeAndDescedants));
|
||||
return new[] { this }.Concat(this.children.SelectMany(c => c.MeAndDescedants));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ size_t load_securities(
|
||||
|
||||
void store_if_new(const std::vector<Security*>& securities) {
|
||||
auto dbptr = open_db();
|
||||
auto insert = utils::prepare_query(dbptr, "insert into securities (key, symbol, venue, type, security_blob, expiration) values (?,?,?,?,?,?)");
|
||||
auto insert = utils::prepare_query(dbptr, "insert or ignore into securities (key, symbol, venue, type, security_blob, expiration) values (?,?,?,?,?,?)");
|
||||
sqlite3_exec_nofail(dbptr, "BEGIN TRANSACTION");
|
||||
std::string buffer;
|
||||
for (const api::Security* psecurity : securities) {
|
||||
@@ -100,10 +100,7 @@ void store_if_new(const std::vector<Security*>& securities) {
|
||||
SQLITE_CHK(sqlite3_bind_int(insert.get(), 4, securitytype_of(*psecurity)), dbptr);
|
||||
SQLITE_CHK(sqlite3_bind_blob(insert.get(), 5, buffer.c_str(), buffer.size(), SQLITE_STATIC), dbptr);
|
||||
SQLITE_CHK(sqlite3_bind_int(insert.get(), 6, expiration(*psecurity).to_short()), dbptr);
|
||||
auto result = sqlite3_step(insert.get());
|
||||
if (result != SQLITE_DONE) {
|
||||
LOG(md, Warn, "Failed to insert security into security db. key={}", psecurity->id());
|
||||
}
|
||||
sqlite3_step(insert.get());
|
||||
sqlite3_reset(insert.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +133,8 @@ void SecurityIDRecorder::work_db() {
|
||||
// Once a security has a key it is public so record it in the security database
|
||||
std::shared_ptr<SecurityIDEntry> temp;
|
||||
vector<std::shared_ptr<SecurityIDEntry>> new_entries;
|
||||
// SPARK-1446 Need to try to add existing securities to the db in case we deleted them by hand
|
||||
vector<std::shared_ptr<SecurityIDEntry>> existing_securities;
|
||||
vector<std::shared_ptr<SecurityIDEntry>> defer;
|
||||
vector<string> new_descriptors;
|
||||
size_t count = 0;
|
||||
@@ -150,6 +152,7 @@ void SecurityIDRecorder::work_db() {
|
||||
if (key != invalid_security_key) {
|
||||
temp->security.set_id(key);
|
||||
temp->callback(temp->security);
|
||||
existing_securities.push_back(move(temp));
|
||||
}
|
||||
else {
|
||||
// always push in parallel
|
||||
@@ -168,7 +171,7 @@ void SecurityIDRecorder::work_db() {
|
||||
}
|
||||
|
||||
|
||||
if (new_entries.size() > 0) {
|
||||
if (!new_entries.empty()) {
|
||||
sqlite3_exec_nofail(_self->db.get(), "BEGIN TRANSACTION");
|
||||
|
||||
for (size_t x = 0; x < new_entries.size(); ++x) {
|
||||
@@ -179,15 +182,22 @@ void SecurityIDRecorder::work_db() {
|
||||
}
|
||||
|
||||
sqlite3_exec_nofail(_self->db.get(), "COMMIT TRANSACTION");
|
||||
}
|
||||
|
||||
// record the securities
|
||||
// record the securities
|
||||
if (!new_entries.empty() || !existing_securities.empty()) {
|
||||
std::vector<Security*> save_these;
|
||||
save_these.reserve(new_entries.size());
|
||||
save_these.reserve(new_entries.size() + existing_securities.size());
|
||||
for (const auto& e : new_entries) {
|
||||
save_these.push_back(&e->security);
|
||||
}
|
||||
for (const auto& e : existing_securities) {
|
||||
save_these.push_back(&e->security);
|
||||
}
|
||||
db::store_if_new(save_these);
|
||||
}
|
||||
|
||||
if (!new_entries.empty()) {
|
||||
for (size_t x = 0; x < new_entries.size(); ++x) {
|
||||
Security& security = new_entries[x]->security;
|
||||
new_entries[x]->callback(security);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
@echo off
|
||||
@setlocal
|
||||
SETLOCAL
|
||||
if "%SPARKVM_PORT%"=="" set SPARKVM_PORT=22
|
||||
if "%1" == "" goto :err
|
||||
echo.
|
||||
ssh bts@sparkvm -p %SPARKVM_PORT% -o StrictHostKeyChecking=no -t "cd /home/bts/spark && git checkout %1 && git reset --hard && git clean -fd && git pull && git submodule update"
|
||||
ENDLOCAL
|
||||
|
||||
goto :eof
|
||||
|
||||
:err
|
||||
echo usage git_switch_linux [branch]
|
||||
Reference in new issue
Block a user