Stand-alone scripts for Smart Office

Here is a solution to write stand-alone scripts for Smart Office. Stand-alone scripts are interesting to create mini applications in Smart Office like widgets, or to create full blown applications like composite applications.

A stand-alone script runs independently of an M3 program, in its own instance, eventually with its own user interface. Whereas a regular script runs as part of an M3 program, for example inside CRS610.

I will illustrate how to write a stand-alone script by creating a Tiling Window Manager widget. It’s a continuation of my previous post on How to tile windows in Smart Office. The result will look like this:

Make sure to read my disclaimer before trying this as I’m using non-public APIs.

First, I will create an empty window with an icon in the Taskbar:

var task = new Task(new Uri('jscript://'));
task.VisibleName = 'Hello World';
var runner = DashboardTaskService.Current.LaunchTask(task, null);
runner.Status = RunnerStatus.Running;
var host = runner.Host;
host.HostTitle = 'Hello World';
host.Show();

The result will look like a regular Smart Office window, albeit empty:

Then, I will use HostType.Widget to change the appearance of the window and make it look like a small widget:

var task = new Task(new Uri('jscript://'));
task.AllowAsShortcut = false;
task.VisibleName = 'Tiling Window Manager';
var runner = DashboardTaskService.Current.LaunchTask(task, HostType.Widget);
runner.Status = RunnerStatus.Running;
var host = runner.Host;
var wrapPanel = new WrapPanel();
host.HostContent = wrapPanel;
host.HostTitle = 'Tiling Window Manager';
host.ResizeMode = ResizeMode.NoResize;
host.Show();
host.Width = 221;
host.Height = 85;

The host’s Width and Height must be set after host.Show().

The result will look like a widget:

Then, I will add three buttons to the widget:

var buttons: String[] = [' ← ''Tile'' → '];
for (var i in buttons) {
    var btn = new Button();
    btn.Content = buttons[i];
    btn.Width = double.NaN; // auto width
    btn.Margin = new Thickness(3, 0, 3, 0); // ltrb
    btn.Padding = new Thickness(7, 0, 7, 0); // ltrb
    wrapPanel.Children.Add(btn);
}
wrapPanel.HorizontalAlignment = HorizontalAlignment.Center;

The result will finally look like the desired widget:

Then, I will add an event handler for the buttons:

btn.add_Click(OnClick);
...
function OnClick(sender: Object, e: RoutedEventArgs) {
    if (sender.Content.Equals(' ← ')) {
        //
    } else if (sender.Content.Equals('Tile')) {
        //
    } else if (sender.Content.Equals(' → ')) {
        //
    }
}

Then, I will add the functions for tiling and shifting the visible windows horizontally on the screen.

var shift = 0;
...
/*
 Tiles the specified windows horizontally accross the screen.
*/
function tileInstances(instances: ArrayList) {
    this.shift = 0;
    for (var i = 0; i < instances.Count; i++) {
        tileInstance(instances[i], i, instances.Count);
    }
}
/*
 Shifts the specified windows horizontally, to the left or to the right.
*/
function shiftInstances(instances: ArrayList, increment: int) {
    this.shift += increment;
    for (var i = 0; i < instances.Count; i++) {
        var n: int = instances.Count;
        var j: int = (((i + this.shift) % n) + n) % n; // fix the JavaScript modulo bug
        tileInstance(instances[i], j, instances.Count);
    }
}

Then, I will deploy the script on the server.

For Grid installations the deploy folder is somewhere like:

\\hostname\d$\Lawson\LifeCycleManager\Service\XYZ\grid\TST\applications\LSO_M3_Adapter\webapps\mne\jscript\

For non-Grid installations the deploy folder is somewhere like:

\\hostname\d$\IBM\WebSphere7\AppServer\profiles\MWPProfile\installedApps\MWPProfileCell\MWP_EAR.ear\MNE.war\jscript\

Then, I will add a shortcut to the Smart Office Canvas to launch the script with this special syntax:

mforms://_runscript?name=TilingWindowManager

My final script is:

import System;
import System.Collections;
import System.Windows;
import System.Windows.Controls;
import Mango.Services;
import Mango.UI.Core;
import Mango.UI.Services;
import MForms;
import Mango.UI;

/*
	Thibaud Lopez Schneider
	Infor
	May 7, 2012

	This script opens a widget to tile and shift the visible windows horizontally in Smart Office.
*/

package MForms.JScript {
	class TilingWindowManager {
		var shift = 0;
		public function Init(element: Object, args: Object, controller : Object, debug : Object) {
			try {
				var task = new Task(new Uri('jscript://'));
				task.AllowAsShortcut = false;
				task.VisibleName = 'Tiling Window Manager';
				var runner = DashboardTaskService.Current.LaunchTask(task, HostType.Widget);
				runner.Status = RunnerStatus.Running;
				var host = runner.Host;
				host.HostContent = CreateWindow();
				host.HostTitle = 'Tiling Window Manager';
				host.ResizeMode = ResizeMode.NoResize;
				host.Show();
				host.Width = 221;
				host.Height = 85;
			} catch(ex: Exception) {
				ConfirmDialog.ShowErrorDialogWithoutCancel(ex.GetType(), ex.Message + '\n' + ex.StackTrace, null);
			}

		}
		function CreateWindow() {
			var wrapPanel: WrapPanel = new WrapPanel();
			var buttons: String[] = [' ← ', 'Tile', ' → '];
			for (var i in buttons) {
				var btn = new Button();
				btn.Content = buttons[i];
				btn.Width = double.NaN; // auto width
				btn.Margin = new Thickness(3, 0, 3, 0); // ltrb
				btn.Padding = new Thickness(7, 0, 7, 0); // ltrb
				wrapPanel.Children.Add(btn);
				btn.add_Click(OnClick);
			}
			wrapPanel.HorizontalAlignment = HorizontalAlignment.Center;
			return wrapPanel;
		}
		/*
			Returns a list of the visible windows.
		*/
		function getVisibleInstances() {
			var instances = MainController.Current.GetInstances();
			var visibleInstances = new ArrayList();
			for (var instance in instances) {
				var window: EmbeddedHostWindow = instance.Host.Implementation;
				if (window.Visibility == Visibility.Visible) {
					visibleInstances.Add(instance);
				}
			}
			return visibleInstances;
		}
		/*
			Tiles the specified window at the specified index relative to the specified count of windows.
		*/
		function tileInstance(instance: InstanceController, i: int, count: int) {
			var window: EmbeddedHostWindow = instance.Host.Implementation;
			window.Width = instance.ParentWindow.Width / count;
			window.Height = instance.ParentWindow.Height;
			DashboardService.Window.SetPosition(new Point(window.Width * i, 0), window);
		}
		/*
			Tiles the specified windows horizontally accross the screen.
		*/
		function tileInstances(instances: ArrayList) {
			this.shift = 0;
			for (var i = 0; i < instances.Count; i++) {
				tileInstance(instances[i], i, instances.Count);
			}
		}
		/*
			Shifts the specified windows horizontally, to the left or to the right.
		*/
		function shiftInstances(instances: ArrayList, increment: int) {
			this.shift += increment;
			for (var i = 0; i < instances.Count; i++) {
 				var n: int = instances.Count;
 				var j: int = (((i + this.shift) % n) + n) % n; // fix the JavaScript modulo bug
 				tileInstance(instances[i], j, instances.Count);
 			}
 		}
 		/*
 			Handles the click on the buttons.
 		*/
 		function OnClick(sender: Object, e: RoutedEventArgs) {
 			try {
 				var visibleInstances = getVisibleInstances();
 				if (visibleInstances.Count > 0) {
					if (sender.Content.Equals(' ← ')) {
						shiftInstances(visibleInstances, -1);
					} else if (sender.Content.Equals('Tile')) {
						tileInstances(visibleInstances);
					} else if (sender.Content.Equals(' → ')) {
						shiftInstances(visibleInstances, +1);
					}
				}
			} catch(ex: Exception) {
				ConfirmDialog.ShowErrorDialogWithoutCancel(ex.GetType(), ex.Message + '\n' + ex.StackTrace, null);
			}
		}
	}
}

Voilà!

This solution showed how to create a stand-alone script in Smart Office, how to create a widget-like script, and how to tile and shift windows.

If you liked this solution, I invite you to subscribe to this blog.

Special thanks to Peter A.J. for the original help.

Published by

thibaudatwork

ex- M3 Technical Consultant

22 thoughts on “Stand-alone scripts for Smart Office”

  1. Interesting, goods work! Do you know what version of Smart Office is required for “Stand-alone scripts”?

    Like

    1. Hi Daniel, I don’t know the version numbers. I wrote the post using Smart Office 10.0.2.0.4. I think the special shortcut mforms://_runscript and the LaunchTask work since Lawson Smart Client 1.x; I started using it around three years ago. I don’t know about the special look and feel HostType.Widget; I started using it around over a year ago with Smart Office 9.x. Nevertheless, not much of the source is using public APIs of Smart Office, so it’s subject to change in the future.

      Like

  2. Great stuff and worked perfectly, thanks.

    Would it be possible to adapt this to add images and use the button/s to cycle through them ?
    That would be awesome.

    Any help would be much appreciated.

    Like

    1. Hi Colm, I don’t understand your requirement about the images and the buttons; I would need more details. It’s technically possible to add images, add buttons, and listen to keyboard input with a script in an M3 program. So I guess your requirement is possible. If you need official support, I invite you to contact me with more details at my Infor email address which you can find at http://www.thibaudlopez.net/

      Like

  3. Hello Thibaud! I copied your script, but the tile only covers about 2/3 of the sceeen. Can I in the code somewhere change the screen resolution? I am using 1600/800. Kind regards, Johan

    Like

    1. Hi Johan, the script is independent of the screen resolution as it’s using instance.ParentWindow.Width and instance.ParentWindow.Height. I tested the script on 1024×768 and 1600×900 and it works fine on both resolutions. Maybe my script doesn’t work for your version of Smart Office, it’s plausible. What exact version number do you have?

      Like

    1. Dear Thibaud,

      My new issue is related with calling scripts from Mashup. This post by CA, which is a partner of our company, has sorted out the issue completely.

      Using {Variable} directly instead of {}{Variable} can eliminate the special character issue, like #, which is used to mean temporary customer address.

      Best Regards,
      Warren

      Like

  4. Nice post (like usual!).

    I used this script as a basis to create a widget that counts the number of error records in GLS037 for the current company/division; the counter is refreshed every x minutes. Is there a way to automatically launch this script defined as a shortcut (mforms://…) as soon as the user logs on ?

    Like

    1. Hi Max, thank you for the feedback. I believe you can run a script on startup but I forgot how. I know you can do &task=mforms://_runcript=myscript.js where you have to properly URI-encode the parameter values in cascade. Maybe you can add _runscript on the canvas. Ask Karin or norpe, they would know. /Thibaud

      Like

      1. You are right. On their blog, it is said that you can add a parameter to the link of the Smart Office URL. Something like this :
        …/mango/MangoClient.application?&task=mforms://_runscript?name=MyScript.js

        Use internet explorer and… it works! The widget is opened automatically as soon as you log in.

        Liked by 1 person

        1. To add arguments you can use the args parameter, for example:
          …/mango/MangoClient.application?&task=mforms://_runscript?name=MyScript.js&args=12345

          Jonathan

          Like

  5. Maxime, I just re-read your requirement. I would use Event Analytics for such counter. Have you considered it? Also, instead of using _runscript in the URL, you can create a feature with Smart Office SDK in C#.

    Like

Leave a comment