public void CreateStandardActions() { var csDialog = new CultureSelectDialog(); // Import Table Wizard...: Add(new Action((s, m) => Governance.AllowCreate(typeof(Table)), (s, m) => ImportTablesWizard.ShowWizard(m), (s, m) => "Import Tables...", true, Context.Model | Context.Tables)); Add(new Action((s, m) => Governance.AllowCreate(typeof(DataColumn)) && m.DataSources.Any(ds => ds.Type == DataSourceType.Provider), (s, m) => ScriptHelper.SchemaCheck(m), (s, m) => "Refresh Table Metadata...", true, Context.Model | Context.Tables)); // Import Table Wizard...: Add(new Action((s, m) => Governance.AllowCreate(typeof(DataColumn)) && s.DirectCount == 1 && s.DataSource is ProviderDataSource, (s, m) => ImportTablesWizard.ShowWizard(m, s.DataSource as ProviderDataSource), (s, m) => "Import Tables...", true, Context.DataSource)); // Schema check: Add(new Action((s, m) => Governance.AllowCreate(typeof(DataColumn)) && s.DirectCount == 1 && m.DataSources.Any(ds => ds.Type == DataSourceType.Provider) && s.DataSource is ProviderDataSource, (s, m) => ScriptHelper.SchemaCheck(s.DataSource as ProviderDataSource), (s, m) => "Refresh Table Metadata...", true, Context.DataSource)); Add(new Action((s, m) => Governance.AllowCreate(typeof(DataColumn)) && s.DirectCount == 1 && m.DataSources.Any(ds => ds.Type == DataSourceType.Provider) && s.Partition.DataSource is ProviderDataSource, (s, m) => ScriptHelper.SchemaCheck(s.Partition), (s, m) => "Refresh Table Metadata...", true, Context.Partition)); Add(new Separator()); // "Create New Display Folder" Add(new Action((s, m) => Governance.AllowEditProperty(s, TOMWrapper.Properties.DISPLAYFOLDER) && s.Count >= 1, (s, m) => { var orgDF = (s.Direct.FirstOrDefault() as IFolderObject)?.GetDisplayFolder(Handler.Tree.Culture); var newDF = string.IsNullOrWhiteSpace(orgDF) ? "New folder" : (orgDF + @"\New folder");; Folder.CreateFolder(s.Table, newDF).Edit().Expand(); s.ReplaceFolder(orgDF, newDF, Handler.Tree.Culture); }, (s, m) => @"Create New\Display Folder", true, Context.TableObject)); Add(new Action((s, m) => Governance.AllowCreate(typeof(CalculationItem)) && s.Count == 1, (s, m) => s.CalculationGroup.AddCalculationItem().Edit(), (s, m) => @"Create New\Calculation Item", false, Context.CalculationGroupTable)); Add(new Separator(@"Create New")); // Add measure: Add(new Action((s, m) => Governance.AllowCreate(typeof(Measure)) && (s.Count == 1 || s.Context.HasX(Context.TableObject)), (s, m) => s.Table.AddMeasure(displayFolder: s.CurrentFolder).Vis().Edit(), (s, m) => @"Create New\Measure", true, Context.CalculationGroupTable | Context.Table | Context.TableObject, Keys.Alt | Keys.D1)); // Add calc column: Add(new Action((s, m) => Governance.AllowCreate(typeof(CalculatedColumn)) && (s.Count == 1 || s.Context.HasX(Context.TableObject)), (s, m) => s.Table.AddCalculatedColumn(displayFolder: s.CurrentFolder).Vis().Edit(), (s, m) => @"Create New\Calculated Column", true, Context.CalculationGroupTable | Context.Table | Context.TableObject, Keys.Alt | Keys.D2)); // Add calc table column: Add(new Action((s, m) => Governance.AllowCreate(typeof(CalculatedTableColumn)) && Handler.SourceType != ModelSourceType.Database && (s.Count == 1 || s.Context.HasX(Context.TableObject)) && (s.Table is CalculatedTable), (s, m) => (s.Table as CalculatedTable).AddCalculatedTableColumn(displayFolder: s.CurrentFolder).Vis().Edit(), (s, m) => @"Create New\Calculated Table Column", true, Context.Table | Context.TableObject)); // Add hierarchy: Add(new Action((s, m) => Governance.AllowCreate(typeof(Hierarchy)) && (s.Count == 1 || s.Context.HasX(Context.TableObject)), (s, m) => s.Table.AddHierarchy(displayFolder: s.CurrentFolder, levels: s.Direct.OfType <Column>().ToArray()).Expand().Vis().Edit(), (s, m) => @"Create New\Hierarchy", true, Context.CalculationGroupTable | Context.Table | Context.TableObject, Keys.Alt | Keys.D3)); // Add data column: Add(new Action((s, m) => Governance.AllowCreate(typeof(DataColumn)) && (s.Count == 1 || s.Context.HasX(Context.TableObject)) && !(s.Table is CalculatedTable), (s, m) => s.Table.AddDataColumn(displayFolder: s.CurrentFolder).Vis().Edit(), (s, m) => @"Create New\Data Column", true, Context.CalculationGroupTable | Context.Table | Context.TableObject, Keys.Alt | Keys.D4)); // Add KPI: Add(new Action((s, m) => Governance.AllowCreate(typeof(KPI)) && s.Count == 1 && !s.Folders.Any(), (s, m) => s.Measure.AddKPI().Edit(), (s, m) => @"Create New\KPI", true, Context.Measure)); Add(new Separator(@"Create New")); Add(new Action( (s, m) => Governance.AllowCreate(typeof(Partition)) && s.Count == 1, (s, m) => Partition.CreateNew(s.Table).Edit(), (s, m) => @"Create New\Partition" + (Handler.CompatibilityLevel >= 1400 ? " (Legacy)" : ""), true, Context.Table)); Add(new Action( (s, m) => Governance.AllowCreate(typeof(MPartition)) && s.Count == 1 && Handler.CompatibilityLevel >= 1400, (s, m) => MPartition.CreateNew(s.Table).Edit(), (s, m) => @"Create New\Partition (Power Query)", true, Context.Table)); Add(new Action( (s, m) => Governance.AllowCreate(typeof(Partition)), (s, m) => Partition.CreateNew(s.Table).Edit(), (s, m) => @"New Partition" + (Handler.CompatibilityLevel >= 1400 ? " (Legacy)" : ""), true, Context.PartitionCollection | Context.Partition)); Add(new Action( (s, m) => Governance.AllowCreate(typeof(MPartition)) && Handler.CompatibilityLevel >= 1400, (s, m) => MPartition.CreateNew(s.Table).Edit(), (s, m) => @"New Partition (Power Query)", true, Context.PartitionCollection | Context.Partition)); Add(new Action((s, m) => Governance.AllowCreate(typeof(Table)) && m.DataSources.Any(), (s, m) => m.AddTable().Vis().Edit(), (s, m) => @"Create New\Table", false, Context.Tables | Context.Model, Keys.Alt | Keys.D5)); Add(new Action((s, m) => Governance.AllowCreate(typeof(CalculatedTable)), (s, m) => m.AddCalculatedTable().Vis().Edit(), (s, m) => @"Create New\Calculated Table", false, Context.Tables | Context.Model, Keys.Alt | Keys.D6)); Add(new Action((s, m) => Governance.AllowCreate(typeof(CalculationGroupTable)) && Handler.CompatibilityLevel >= 1470, (s, m) => m.AddCalculationGroup().Vis().Edit(), (s, m) => @"Create New\Calculation Group", false, Context.Tables | Context.Model, Keys.Alt | Keys.D7)); Add(new Action((s, m) => Governance.AllowCreate(typeof(CalculationItem)) && s.Count == 1, (s, m) => s.CalculationGroup.AddCalculationItem().Edit(), (s, m) => @"New Calculation Item", false, Context.CalculationItemCollection)); Add(new Action((s, m) => Governance.AllowCreate(typeof(CalculationItem)), (s, m) => s.CalculationItems.First().CalculationGroupTable.AddCalculationItem().Edit(), (s, m) => @"New Calculation Item", false, Context.CalculationItem)); Add(new Action((s, m) => Governance.AllowCreate(typeof(ProviderDataSource)), (s, m) => m.AddDataSource().Edit(), (s, m) => @"Create New\Data Source (Legacy)", false, Context.DataSources | Context.Model)); Add(new Action((s, m) => Governance.AllowCreate(typeof(StructuredDataSource)) && Handler.CompatibilityLevel >= 1400, (s, m) => m.AddStructuredDataSource().Edit(), (s, m) => @"Create New\Data Source (Power Query)", false, Context.DataSources | Context.Model)); Add(new Action((s, m) => Governance.AllowCreate(typeof(Perspective)), (s, m) => m.AddPerspective().Edit(), (s, m) => @"Create New\Perspective", false, Context.Model | Context.Perspectives | Context.Perspective)); Add(new Action((s, m) => Governance.AllowCreate(typeof(NamedExpression)), (s, m) => m.AddExpression().Edit(), (s, m) => @"Create New\Shared Expression", false, Context.Model | Context.Expressions | Context.Expression)); Add(new Action((s, m) => Governance.AllowCreate(typeof(SingleColumnRelationship)) && m.Tables.Count(t => t.Columns.Any()) >= 2, (s, m) => m.AddRelationship().Edit(), (s, m) => @"Create New\Relationship", false, Context.Relationship | Context.Relationships | Context.Model)); Add(new Action((s, m) => Governance.AllowCreate(typeof(ModelRole)), (s, m) => m.AddRole().Edit(), (s, m) => @"Create New\Role", false, Context.Model | Context.Roles | Context.Role)); Add(new Action((s, m) => Governance.AllowCreate(typeof(Culture)), (s, m) => { var res = csDialog.ShowDialog(); if (res == DialogResult.OK) { m.AddTranslation(csDialog.SelectedCulture.Name).Edit(); } }, (s, m) => @"Create New\Translation", true, Context.Model | Context.Translations | Context.Translation)); Add(new CreateRelationshipAction(CreateRelationshipDirection.To)); Add(new CreateRelationshipAction(CreateRelationshipDirection.From)); // "Add to Hierarchy..." Add(new MultiAction((s, m, p) => Governance.AllowEditProperty(ObjectType.Hierarchy, TOMWrapper.Properties.LEVELS) && ( // Action enabled only when table contains at least one hierarchy: ((p == null) && s.Table.Hierarchies.Any()) || // ...and none of the selected columns are already present as levels in the hierarchy: ((p != null) && !(p as Hierarchy).Levels.Select(l => l.Column).Intersect(s.Columns).Any())), (s, m, p) => (p as Hierarchy).AddLevels(s.Columns), (s, m, p) => (p as Hierarchy).Name, (s, m) => s.Table.Hierarchies.AsEnumerable(), "Add to Hierarchy...", true, Context.Column)); Add(new Separator()); // Relationship actions: Add(new Action((s, m) => Governance.AllowEditProperty(ObjectType.Relationship, TOMWrapper.Properties.ISACTIVE) && s.SingleColumnRelationships.Any(o => !o.IsActive), (s, m) => s.SingleColumnRelationships.IsActive = true, (s, m) => "Activate", true, Context.Relationship)); Add(new Action((s, m) => Governance.AllowEditProperty(ObjectType.Relationship, TOMWrapper.Properties.ISACTIVE) && s.SingleColumnRelationships.Any(o => o.IsActive), (s, m) => s.SingleColumnRelationships.IsActive = false, (s, m) => "Deactivate", true, Context.Relationship)); // Reverse relationship: Add(new Action((s, m) => Governance.AllowEditProperty(ObjectType.Relationship, TOMWrapper.Properties.FROMCOLUMN), (s, m) => s.SingleColumnRelationships.ForEach(r => { var fc = r.FromColumn; r.FromColumn = null; var tc = r.ToColumn; r.ToColumn = null; r.FromColumn = tc; r.ToColumn = fc; }), (s, m) => "Reverse direction", false, Context.Relationship)); // Visibility and perspectives Add(new Action((s, m) => Governance.AllowEditProperty(s, TOMWrapper.Properties.ISHIDDEN) && s.OfType <IHideableObject>().Any(o => o.IsHidden), (s, m) => s.IsHidden = false, (s, m) => "Make visible", true, Context.DataObjects, Keys.Control | Keys.U)); Add(new Action((s, m) => Governance.AllowEditProperty(s, TOMWrapper.Properties.ISHIDDEN) && s.OfType <IHideableObject>().Any(o => !o.IsHidden), (s, m) => s.IsHidden = true, (s, m) => "Make invisible", true, Context.DataObjects, Keys.Control | Keys.I)); Add(new Action((s, m) => Governance.AllowCreate(typeof(Perspective)) && s.OfType <ITabularPerspectiveObject>().Any(), (s, m) => s.ShowInAllPerspectives(), (s, m) => @"Show in Perspectives\All Perspectives", true, Context.DataObjects)); Add(new Action((s, m) => Governance.AllowCreate(typeof(Perspective)) && s.OfType <ITabularPerspectiveObject>().Any(), (s, m) => s.HideInAllPerspectives(), (s, m) => @"Hide in Perspectives\All Perspectives", true, Context.DataObjects)); Add(new Separator(@"Show in Perspectives")); Add(new Separator(@"Hide in Perspectives")); Add(new MultiAction((s, m, p) => Governance.AllowCreate(typeof(Perspective)) && s.OfType <ITabularPerspectiveObject>().Any(obj => p == null || !obj.InPerspective[p as Perspective]), (s, m, p) => s.ShowInPerspective(p as Perspective), (s, m, p) => (p as Perspective).Name, (s, m) => m.Perspectives, "Show in Perspectives", true, Context.DataObjects)); Add(new MultiAction((s, m, p) => Governance.AllowCreate(typeof(Perspective)) && s.OfType <ITabularPerspectiveObject>().Any(obj => p == null || obj.InPerspective[p as Perspective]), (s, m, p) => s.HideInPerspective(p as Perspective), (s, m, p) => (p as Perspective).Name, (s, m) => m.Perspectives, "Hide in Perspectives", true, Context.DataObjects)); // Rename Dialogs Add(new Separator()); // "Duplicate Table Object"; Add(new Action((s, m) => Governance.AllowCreate(s), (s, m) => s.ForEach(i => { var obj = (i as IClonableObject).Clone(includeTranslations: i is ITranslatableObject); if (s.Count == 1) { obj.Edit(); // Focuses the cloned item in the tree, and lets the user edit its name } }), (s, m) => "Duplicate " + s.Summary(), true, Context.TableObject | Context.Partition | Context.CalculationItem)); // "Duplicate Table"; Add(new Action((s, m) => Governance.AllowCreate(s) && s.Count == 1, (s, m) => s.Table.Clone().Edit(), (s, m) => "Duplicate Table", true, Context.Table)); Add(new Action((s, m) => Governance.AllowCreate(typeof(CalculationGroupTable)) && s.Count == 1, (s, m) => s.CalculationGroup.Clone().Edit(), (s, m) => "Duplicate Calculation Group", true, Context.CalculationGroupTable)); // "Duplicate Translation"; Add(new Action((s, m) => Governance.AllowCreate(typeof(Culture)) && s.Count == 1, (s, m) => s.ForEach(i => { var res = csDialog.ShowDialog(); if (res == DialogResult.OK) { (i as IClonableObject).Clone(csDialog.SelectedCulture.Name, false).Edit(); } }), (s, m) => "Duplicate " + s.Summary(), true, Context.Translation)); // "Duplicate Role / Perspective": Add(new Action((s, m) => Governance.AllowCreate(typeof(Perspective)) && s.Count == 1, (s, m) => s.ForEach(i => (i as IClonableObject).Clone(null, true).Edit()), (s, m) => "Duplicate Perspective", true, Context.Perspective)); Add(new Action((s, m) => Governance.AllowCreate(typeof(ModelRole)) && s.Count == 1, (s, m) => s.ForEach(i => (i as IClonableObject).Clone(null, true).Edit()), (s, m) => "Duplicate Role", true, Context.Role)); // Batch Rename Add(new Action((s, m) => Governance.AllowEditProperty(s, TOMWrapper.Properties.NAME) && s.DirectCount > 1, (s, m) => { var form = Dialogs.ReplaceForm.Singleton; form.Text = "Batch Rename - (" + s.Summary() + " selected)"; var res = form.ShowDialog(); if (res == DialogResult.Cancel) { return; } // TODO: Add options for match case and whole word only s.Rename(form.Pattern, form.ReplaceWith, form.RegEx, form.IncludeTranslations); }, (s, m) => "Batch Rename...", true, Context.DataObjects | Context.Level | Context.CalculationItem | Context.Partition, Keys.F2) { ToolTip = "Opens a dialog that lets you rename all the selected objects at once. Folders are not renamed, but objects inside folders are." }); // Batch Rename Children Add(new Action((s, m) => Governance.AllowEditProperty(new[] { ObjectType.Measure, ObjectType.Column, ObjectType.Hierarchy, ObjectType.Level }, TOMWrapper.Properties.NAME) && (s.Context == Context.Table || s.Direct.Any(i => i is Folder) || s.Context == Context.PartitionCollection), (s, m) => { var form = Dialogs.ReplaceForm.Singleton; var sel = new UISelectionList <ITabularNamedObject>( s.Direct.OfType <ITabularObjectContainer>().SelectMany(t => t.GetChildren()) .Concat(s.Hierarchies.SelectMany(t => t.Levels)) .OfType <ITabularNamedObject>()); form.Text = "Batch Rename - (" + sel.Summary() + ")"; var res = form.ShowDialog(); if (res == DialogResult.Cancel) { return; } // TODO: Add options for match case and whole word only sel.Rename(form.Pattern, form.ReplaceWith, form.RegEx, form.IncludeTranslations); }, (s, m) => "Batch Rename Children...", true, Context.DataObjects | Context.PartitionCollection, Keys.Shift | Keys.F2) { ToolTip = "Opens a dialog that lets you rename all children of the selected objects at once. Folders are not renamed, but objects inside folders are." }); // Delete Action Delete = new Action((s, m) => Governance.AllowDelete(s) && s.Count >= 1, (s, m) => { if (s.Count == 1) { // Handle single-object deletion: string message; if (!s.First().CanDelete(out message)) { MessageBox.Show(message, s.Summary() + " cannot be deleted.", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } else if (!string.IsNullOrEmpty(message)) { var mr = MessageBox.Show(message, "Confirm deletion", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (mr == DialogResult.Cancel) { return; } } } else { // Handle multi-object deletion: string message; if (s.Any(o => !o.CanDelete(out message))) { var mr = MessageBox.Show("One or more of the selected objects cannot be deleted. Proceed?", "Unable to delete one or more objects.", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (mr == DialogResult.Cancel) { return; } } else { // Confirm deletion of multiple objects just in case... var mr = MessageBox.Show("Are you sure you want to delete the selected objects?", s.Summary(), MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (mr == DialogResult.Cancel) { return; } } } s.Delete(); }, (s, m) => "Delete " + s.Summary() + "...", true, Context.SingularObjects ^ Context.Model); Add(Delete); Add(new Separator()); // Select Columns (aka. Import Table Wizard) and Refresh Table Metadata: Add(new Action((s, m) => Governance.AllowCreate(typeof(DataColumn)) && s.DirectCount == 1 && s.Table.Partitions[0].DataSource is ProviderDataSource, (s, m) => ImportTablesWizard.ShowWizard(s.Table), (s, m) => "Select Columns...", true, Context.Table)); Add(new Action((s, m) => Governance.AllowCreate(typeof(DataColumn)) && s.DirectCount == 1 && m.DataSources.Any(ds => ds.Type == DataSourceType.Provider) && s.Table.Partitions.Count > 0 && s.Table.Partitions[0].DataSource is ProviderDataSource, (s, m) => ScriptHelper.SchemaCheck(s.Table), (s, m) => "Refresh Table Metadata...", true, Context.Table)); Add(new Separator()); Add(new Action((s, m) => m.Cultures.Count > 0, (s, m) => UIController.Current.Translations_ExportAll(), (s, m) => "Export translations...", false, Context.Translations | Context.Tool)); Add(new Action((s, m) => Governance.AllowCreate(typeof(Culture)) && true, (s, m) => UIController.Current.Translations_Import(), (s, m) => "Import translations...", true, Context.Translations | Context.Tool)); Add(new Action((s, m) => true, (s, m) => UIController.Current.Translations_ExportSelected(), (s, m) => string.Format("Export {0} translation{1}...", s.Count, s.Count == 1 ? "" : "s"), true, Context.Translation)); // Table actions: Add(new Action((s, m) => Governance.AllowEditProperty(ObjectType.Column, TOMWrapper.Properties.ISKEY) && Governance.AllowEditProperty(ObjectType.Column, TOMWrapper.Properties.DATACATEGORY) && s.DirectCount == 1 && s.Table.Columns.Any(c => c.DataType == DataType.DateTime), (s, m) => UIController.Current.MarkAsDateTableDialog.Go(s.Table), (s, m) => "Mark as Date Table...", true, Context.Table)); // Show dependencies... Add(new Action((s, m) => s.DirectCount == 1 && s.Direct.First() is IDaxObject, (s, m) => { UIController.Current.ShowDependencies(s.Direct.First() as IDaxObject); }, (s, m) => @"Show &dependencies...", true, Context.Table | Context.TableObject | Context.CalculationItem, Keys.F3)); // Filter related... // TODO /*Add(new Action((s, m) => s.DirectCount == 1, (s, m) => UIController.Current.ApplyFilter(":RelatedTables.Any(Name = \"" + s.Table.Name + "\")"), * (s, m) => @"Filter &related", true, Context.Table));*/ // Script actions: Add(new Action((s, m) => s.DirectCount == 1, (s, m) => Clipboard.SetText(Scripter.ScriptCreateOrReplace(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Create or Replace\To clipboard", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => Clipboard.SetText(Scripter.ScriptCreate(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Create\To clipboard", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => Clipboard.SetText(Scripter.ScriptAlter(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Alter\To clipboard", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => Clipboard.SetText(Scripter.ScriptDelete(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Delete\To clipboard", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => SaveScriptToFile(Scripter.ScriptCreateOrReplace(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Create or Replace\To file...", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => SaveScriptToFile(Scripter.ScriptCreate(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Create\To file...", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => SaveScriptToFile(Scripter.ScriptAlter(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Alter\To file...", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => SaveScriptToFile(Scripter.ScriptDelete(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Delete\To file...", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount > 1, (s, m) => Clipboard.SetText(Scripter.ScriptMergePartitions(s.OfType <Partition>().ToList())), (s, m) => @"Script\Merge Partitions\To clipboard", true, Context.Partition)); Add(new Action((s, m) => s.DirectCount > 1, (s, m) => SaveScriptToFile(Scripter.ScriptMergePartitions(s.OfType <Partition>().ToList())), (s, m) => @"Script\Merge Partitions\To file...", true, Context.Partition)); Add(new RefreshAction(true)); Add(new RefreshAction(false)); }
public void CreateStandardActions() { var csDialog = new CultureSelectDialog(); // "Create New" Add(new Action((s, m) => s.Count >= 1 && !Handler.UsePowerBIGovernance, (s, m) => { var disp = (s.FirstOrDefault() as IDetailObject)?.DisplayFolder; disp = string.IsNullOrWhiteSpace(disp) ? "New folder" : (disp + @"\New folder");; Folder.CreateFolder(s.Table, disp).Edit(); s.DisplayFolder = disp; }, (s, m) => @"Create New\Display Folder", true, Context.TableObject)); Add(new Action((s, m) => s.Count == 1 && s.Types == Types.Measure, (s, m) => s.Measure.AddKPI().Edit(), (s, m) => @"Create New\KPI", true, Context.DataObjects)); Add(new Action((s, m) => true, (s, m) => s.Table.AddMeasure(displayFolder: s.CurrentFolder).Edit(), (s, m) => @"Create New\Measure", true, Context.Table | Context.TableObject)); Add(new Action((s, m) => true, (s, m) => s.Table.AddCalculatedColumn(displayFolder: s.CurrentFolder).Edit(), (s, m) => @"Create New\Calculated Column", true, Context.Table | Context.TableObject)); Add(new Action((s, m) => !Handler.UsePowerBIGovernance, (s, m) => s.Table.AddDataColumn(displayFolder: s.CurrentFolder).Edit(), (s, m) => @"Create New\Data Column", true, Context.Table | Context.TableObject)); Add(new Action((s, m) => true || s.Direct.OfType <Column>().Any(), (s, m) => s.Table.AddHierarchy(displayFolder: s.CurrentFolder, levels: s.Direct.OfType <Column>().ToArray()).Expand().Edit(), (s, m) => @"Create New\Hierarchy", true, Context.Table | Context.TableObject)); Add(new Separator(@"Create New")); Add(new Action( (s, m) => (s.Context == Context.Partition || (s.Context == Context.Table && s.Count == 1)) && !Handler.UsePowerBIGovernance, (s, m) => Partition.CreateNew(s.Context == Context.Partition ? s.Partitions.First().Table : s.Table).Edit(), (s, m) => @"Create New\Partition", true, Context.Table | Context.Partition)); Add(new Action((s, m) => !Handler.UsePowerBIGovernance, (s, m) => m.AddDataSource().Edit(), (s, m) => @"Create New\Data Source", false, Context.DataSources | Context.Model)); Add(new Action((s, m) => Handler.CompatibilityLevel >= 1400 && !Handler.UsePowerBIGovernance, (s, m) => m.AddStructuredDataSource().Edit(), (s, m) => @"Create New\Structured Data Source", false, Context.DataSources | Context.Model)); Add(new Action((s, m) => !Handler.UsePowerBIGovernance, (s, m) => m.AddPerspective().Edit(), (s, m) => @"Create New\Perspective", false, Context.Model | Context.Perspectives | Context.Perspective)); Add(new Action((s, m) => !Handler.UsePowerBIGovernance, (s, m) => m.AddExpression().Edit(), (s, m) => @"Create New\Shared Expression", false, Context.Model | Context.Expressions | Context.Expression)); Add(new Action((s, m) => m.Tables.Count(t => t.Columns.Any()) >= 2, (s, m) => m.AddRelationship().Edit(), (s, m) => @"Create New\Relationship", false, Context.Relationship | Context.Relationships | Context.Model)); Add(new Action((s, m) => true, (s, m) => m.AddRole().Edit(), (s, m) => @"Create New\Role", false, Context.Model | Context.Roles | Context.Role)); Add(new Action((s, m) => m.DataSources.Any() && !Handler.UsePowerBIGovernance, (s, m) => m.AddTable().Edit(), (s, m) => @"Create New\Table", false, Context.Tables | Context.Model)); Add(new Action((s, m) => true, (s, m) => m.AddCalculatedTable().Edit(), (s, m) => @"Create New\Calculated Table", false, Context.Tables | Context.Model)); Add(new Action((s, m) => !Handler.UsePowerBIGovernance, (s, m) => { var res = csDialog.ShowDialog(); if (res == DialogResult.OK) { m.AddTranslation(csDialog.SelectedCulture.Name).Edit(); } }, (s, m) => @"Create New\Translation", true, Context.Model | Context.Translations | Context.Translation)); Add(new CreateRelationshipAction(CreateRelationshipDirection.To)); Add(new CreateRelationshipAction(CreateRelationshipDirection.From)); // "Add to Hierarchy..." Add(new MultiAction((s, m, p) => // Action enabled only when table contains at least one hierarchy: ((p == null) && s.Table.Hierarchies.Any()) || // ...and none of the selected columns are already present as levels in the hierarchy: ((p != null) && !(p as Hierarchy).Levels.Select(l => l.Column).Intersect(s.Columns).Any()), (s, m, p) => (p as Hierarchy).AddLevels(s.Columns), (s, m, p) => (p as Hierarchy).Name, (s, m) => s.Table.Hierarchies.AsEnumerable(), "Add to Hierarchy...", true, Context.Column)); Add(new Separator()); // Visibility and perspectives Add(new Action((s, m) => s.OfType <IHideableObject>().Any(o => o.IsHidden), (s, m) => s.IsHidden = false, (s, m) => "Make visible", true, Context.TableObject | Context.Table)); Add(new Action((s, m) => s.OfType <IHideableObject>().Any(o => !o.IsHidden), (s, m) => s.IsHidden = true, (s, m) => "Make invisible", true, Context.TableObject | Context.Table)); Add(new Action((s, m) => s.OfType <ITabularPerspectiveObject>().Any() && !Handler.UsePowerBIGovernance, (s, m) => s.ShowInAllPerspectives(), (s, m) => @"Show in Perspectives\All Perspectives", true, Context.TableObject | Context.Table)); Add(new Action((s, m) => s.OfType <ITabularPerspectiveObject>().Any() && !Handler.UsePowerBIGovernance, (s, m) => s.HideInAllPerspectives(), (s, m) => @"Hide in Perspectives\All Perspectives", true, Context.TableObject | Context.Table)); Add(new Separator(@"Show in Perspectives")); Add(new Separator(@"Hide in Perspectives")); Add(new MultiAction((s, m, p) => s.OfType <ITabularPerspectiveObject>().Any(obj => p == null || !obj.InPerspective[p as Perspective]) && !Handler.UsePowerBIGovernance, (s, m, p) => s.ShowInPerspective(p as Perspective), (s, m, p) => (p as Perspective).Name, (s, m) => m.Perspectives, "Show in Perspectives", true, Context.TableObject | Context.Table)); Add(new MultiAction((s, m, p) => s.OfType <ITabularPerspectiveObject>().Any(obj => p == null || obj.InPerspective[p as Perspective]) && !Handler.UsePowerBIGovernance, (s, m, p) => s.HideInPerspective(p as Perspective), (s, m, p) => (p as Perspective).Name, (s, m) => m.Perspectives, "Hide in Perspectives", true, Context.TableObject | Context.Table)); // Rename Dialogs Add(new Separator()); // "Duplicate Table Object"; Add(new Action((s, m) => true, (s, m) => s.ForEach(i => { var obj = (i as IClonableObject).Clone(includeTranslations: i is ITranslatableObject); if (s.Count == 1) { obj.Edit(); // Focuses the cloned item in the tree, and lets the user edit its name } }), (s, m) => "Duplicate " + s.Summary(), true, Context.TableObject | Context.Partition)); // "Duplicate Table"; Add(new Action((s, m) => s.Count == 1 && !Handler.UsePowerBIGovernance, (s, m) => s.Table.Clone().Edit(), (s, m) => "Duplicate Table", true, Context.Table)); // "Duplicate Translation"; Add(new Action((s, m) => s.Count == 1, (s, m) => s.ForEach(i => { var res = csDialog.ShowDialog(); if (res == DialogResult.OK) { (i as IClonableObject).Clone(csDialog.SelectedCulture.Name, false).Edit(); } }), (s, m) => "Duplicate " + s.Summary(), true, Context.Translation)); // "Duplicate Role / Perspective": Add(new Action((s, m) => s.Count == 1, (s, m) => s.ForEach(i => (i as IClonableObject).Clone(null, true).Edit()), (s, m) => "Duplicate " + s.Summary(), true, Context.Role | Context.Perspective)); // Batch Rename Add(new Action((s, m) => s.Count > 1, (s, m) => { var form = Dialogs.ReplaceForm.Singleton; form.Text = "Batch Rename - (" + s.Summary() + " selected)"; var res = form.ShowDialog(); if (res == DialogResult.Cancel) { return; } // TODO: Add options for match case and whole word only s.Rename(form.Pattern, form.ReplaceWith, form.RegEx, form.IncludeTranslations); }, (s, m) => "Batch Rename...", true, Context.Table | Context.TableObject | Context.Level) { ToolTip = "Opens a dialog that lets you rename all the selected objects at once. Folders are not renamed, but objects inside folders are." }); // Batch Rename Children Add(new Action((s, m) => true, (s, m) => { var form = Dialogs.ReplaceForm.Singleton; var sel = new UISelectionList <ITabularNamedObject>( s.Tables.SelectMany(t => t.GetChildren()) .Concat(s.Hierarchies.SelectMany(t => t.Levels))); form.Text = "Batch Rename - (" + sel.Summary() + ")"; var res = form.ShowDialog(); if (res == DialogResult.Cancel) { return; } // TODO: Add options for match case and whole word only sel.Rename(form.Pattern, form.ReplaceWith, form.RegEx, form.IncludeTranslations); }, (s, m) => "Batch Rename Children...", true, Context.Table) { ToolTip = "Opens a dialog that lets you rename all children of the selected objects at once. Folders are not renamed, but objects inside folders are." }); // Delete Action Delete = new Action((s, m) => s.Count >= 1, (s, m) => { if (s.Count == 1) { // Handle single-object deletion: string message; if (!s.First().CanDelete(out message)) { MessageBox.Show(message, s.Summary() + " cannot be deleted.", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } else if (!string.IsNullOrEmpty(message)) { var mr = MessageBox.Show(message, "Confirm deletion", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (mr == DialogResult.Cancel) { return; } } } else { // Handle multi-object deletion: string message; if (s.Any(o => !o.CanDelete(out message))) { var mr = MessageBox.Show("One or more of the selected objects cannot be deleted. Proceed?", "Unable to delete one or more objects.", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (mr == DialogResult.Cancel) { return; } } else { // Confirm deletion of multiple objects just in case... var mr = MessageBox.Show("Are you sure you want to delete the selected objects?", s.Summary(), MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (mr == DialogResult.Cancel) { return; } } } s.Delete(); }, (s, m) => "Delete " + s.Summary() + "...", true, Context.SingularObjects ^ Context.Model); Add(Delete); Add(new Separator()); Add(new Action((s, m) => m.Cultures.Count > 0, (s, m) => UIController.Current.Translations_ExportAll(), (s, m) => "Export translations...", false, Context.Translations | Context.Tool)); Add(new Action((s, m) => true, (s, m) => UIController.Current.Translations_Import(), (s, m) => "Import translations...", true, Context.Translations | Context.Tool)); Add(new Action((s, m) => true, (s, m) => UIController.Current.Translations_ExportSelected(), (s, m) => string.Format("Export {0} translation{1}...", s.Count, s.Count == 1 ? "" : "s"), true, Context.Translation)); // Show dependencies... Add(new Action((s, m) => s.DirectCount == 1 && s.Direct.First() is IDaxObject, (s, m) => { UIController.Current.ShowDependencies(s.Direct.First() as IDaxObject); }, (s, m) => @"Show dependencies...", true, Context.Table | Context.TableObject)); // Reverse relationship: Add(new Action((s, m) => true, (s, m) => s.SingleColumnRelationships.ForEach(r => { var fc = r.FromColumn; r.FromColumn = null; var tc = r.ToColumn; r.ToColumn = null; r.FromColumn = tc; r.ToColumn = fc; }), (s, m) => "Reverse direction", false, Context.Relationship)); // Script actions: Add(new Action((s, m) => s.DirectCount == 1, (s, m) => Clipboard.SetText(Scripter.ScriptCreateOrReplace(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Create or Replace\To clipboard", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => Clipboard.SetText(Scripter.ScriptCreate(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Create\To clipboard", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => Clipboard.SetText(Scripter.ScriptAlter(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Alter\To clipboard", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => Clipboard.SetText(Scripter.ScriptDelete(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Delete\To clipboard", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => SaveScriptToFile(Scripter.ScriptCreateOrReplace(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Create or Replace\To file...", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => SaveScriptToFile(Scripter.ScriptCreate(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Create\To file...", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => SaveScriptToFile(Scripter.ScriptAlter(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Alter\To file...", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount == 1, (s, m) => SaveScriptToFile(Scripter.ScriptDelete(s.OfType <TabularNamedObject>().FirstOrDefault())), (s, m) => @"Script\Delete\To file...", true, Context.Scriptable)); Add(new Action((s, m) => s.DirectCount > 1, (s, m) => Clipboard.SetText(Scripter.ScriptMergePartitions(s.OfType <Partition>().ToList())), (s, m) => @"Script\Merge Partitions\To clipboard", true, Context.Partition)); Add(new Action((s, m) => s.DirectCount > 1, (s, m) => SaveScriptToFile(Scripter.ScriptMergePartitions(s.OfType <Partition>().ToList())), (s, m) => @"Script\Merge Partitions\To file...", true, Context.Partition)); Add(new RefreshAction(true)); Add(new RefreshAction(false)); }