Saturday 5 May 2018

MS Office Word - copy text and it puts OLE bookmarks - a handy macro to stop this

Have you recently found that copying something in Microsoft Word causes the copied text to have grey square brackets surround it?

These brackets are showing because the "show bookmarks" option is enabled. Indeed you may want to keep that option enabled, as you have other bookmarks that you want to see while editing.

The new bookmarks are being created with a certain naming format, they are sequentially numbered and are named like "OLE_LINK##". These bookmarks are automatically created by Word.

Here is a macro to automatically delete the bookmarks straight after they're created. It doesn't delete other bookmarks, only the ones that start with "OLE_LINK".

The best place to put this is in the Normal.dot file. To find this, enable the Developer tab in the ribbon, and then click on the Visual Basic button. In there you can find a file called "ThisDocument" inside the "Normal" heading. Inside that file just put the following code and then save it.

Sub EditCopy()
    Selection.Copy
    DoEvents
    Application.OnTime Now + TimeValue("00:00:01"), "DeleteOleBookmarks"
End Sub

Sub DeleteOleBookmarks()
    Dim bmIndex As Integer
    Dim bmType As String
    DoEvents
    For bmIndex = ActiveDocument.Bookmarks.Count To 1 Step -1
        bmType = UCase(Left(ActiveDocument.Bookmarks(bmIndex).Name, 8))
        If bmType = "OLE_LINK" Then
            ActiveDocument.Bookmarks(bmIndex).Delete
        End If
    Next bmIndex
End Sub

Tuesday 1 May 2018

Keep the monitor display brightness the same when the power source is changed to battery/charging

When using a laptop you usually have to adjust the screen's brightness based on the ambient lighting conditions. However, when plugging/unplugging the power supply Windows will change the brightness again. This can be a poor user experience, for example the reason for plugging it in was just to charge it, not to change the brightness.

Here is a PowerShell v3.0+ script and Windows Task Scheduler config file for fixing this issue. The schedule will run at system startup, and just runs the script. The PowerShell script will register a script block to some Windows events. One is the brightness change event, so when the user manually changes the screen brightness it will save that value to a temp file. The other event is when the power supply is changed to battery/charging, it will change the value back from the saved temp file.

Here's the PowerShell script, just copy this into a Notepad and save as "C:\Battery power display brightness.ps1".


Sunday 1 April 2018

PowerShell modules

Overview of PowerShell Modules

A module in PowerShell is basically a script with exposed members, such as Functions and Variables. It's best for a specific module to serve a singular purpose. There are four kinds of PowerShell module. (As documented in this Microsoft link: click here.)
  • Script Modules -- PSM1 files that usually contain functions, but can contain any valid PowerShell code.
  • Binary Modules -- Compiled DLL files, which can be created in .NET languages like C#, F# or VB.
  • Manifest Modules -- These are Script Modules which contain a manifest.
  • Dynamic Modules -- In memory modules which haven't been persisted to permanent storage.

How To Create A PowerShell Module

At the basic level, a PowerShell module is a text file with a ".psm1" extension. Here's an example:
  • # A private variable.
  • $prefix = 'Some text'

  • Function Get-Prefix
  • {
  •     $prefix
  • }

  • Function Append-Prefix ($suffix)
  • {
  •     $prefix + $suffix
  • }

  • Function HiddenFunc
  • {
  •     # do something...
  • }

  • Export-ModuleMember -Function '*-*'

Here the functions "Get-Prefix" and "Append-Prefix" are exposed, but the variable "$prefix" and function "HiddenFunc" are not exposed.

How To Use A PowerShell Module

A module can be imported into the PowerShell console directly, and also into script files. Importing the module brings all of the functions and variables into that PowerShell session. To do this you can import a module called "C:\foobar.psm1" in the following way:
Import-Module C:\foobar.psm1

Friday 2 March 2018

Android Preferences Time Picker

When developing the preferences page for an Android app you can add a time picker with this class.

import android.content.*;
import android.content.res.*;
import android.preference.*;
import android.util.*;
import android.view.*;
import android.widget.*;
import java.text.*;
import java.util.*;

/*
* Use in preferences.xml like this:
*   <net.intrepidis.library.prefs.TimePreference
*       android:key="mytime_preference"
*       android:title="@string/mytime_preftitle"
*       android:summary="@string/mytime_prefsummary"
*       android:defaultValue="00:10"
*       positiveButtonText="@string/ok_button"
*       negativeButtonText="Close me" />
*/
public class TimePreference extends DialogPreference {
private final Calendar calendar;
private final TimePicker picker;

public TimePreference(final Context ctx) {
this(ctx, null);
}

public TimePreference(final Context ctx, final AttributeSet attrs) {
this(ctx, attrs, android.R.attr.dialogPreferenceStyle);
}

public TimePreference(final Context ctx, final AttributeSet attrs, final int defStyle) {
super(ctx, attrs, defStyle);

calendar = new GregorianCalendar();
picker = new TimePicker(ctx);

// Set 24-hour display.
final boolean is24 = is24HourSystemDateSetting();
picker.setIs24HourView(is24);

// Positive button text.
{
final int resId = attrs.getAttributeResourceValue(
null, "positiveButtonText", -1);
final String text =
attrs.getAttributeValue(null, "positiveButtonText");
if (resId != -1) {
setPositiveButtonText(resId);
} else if (text != null) {
setPositiveButtonText(text);
}
}

// Negative button text.
{
final int resId = attrs.getAttributeResourceValue(
null, "negativeButtonText", -1);
final String text =
attrs.getAttributeValue(null, "negativeButtonText");
if (resId != -1) {
setNegativeButtonText(resId);
} else if (text != null) {
setNegativeButtonText(text);
}
}
}

@Override
protected View onCreateDialogView() {
return picker;
}

@Override
protected void onBindDialogView(final View v) {
super.onBindDialogView(v);
picker.setCurrentHour(calendar.get(Calendar.HOUR_OF_DAY));
picker.setCurrentMinute(calendar.get(Calendar.MINUTE));
}

@Override
protected void onDialogClosed(final boolean positiveResult) {
super.onDialogClosed(positiveResult);

if (positiveResult) {
calendar.set(Calendar.HOUR_OF_DAY, picker.getCurrentHour());
calendar.set(Calendar.MINUTE, picker.getCurrentMinute());

setSummary(getSummary());

final long millis = calendar.getTimeInMillis();
if (callChangeListener(millis)) {
persistLong(millis);
notifyChanged();
}
}
}

@Override
protected Object onGetDefaultValue(final TypedArray a, final int index) {
try {
final String s = a.getString(index);
// Use 24-hour format for the default setting.
final DateFormat df = new SimpleDateFormat("HH:mm");
final Date date = df.parse(s);
final long l = date.getTime();
return l;
} catch (ParseException e) {
return null;
}
}

@Override
protected void onSetInitialValue(
final boolean restoreValue,
final Object defaultValue
) {
final long def =
defaultValue == null
? System.currentTimeMillis()
: (long)defaultValue;

final long time;
if (restoreValue) {
time = getPersistedLong(def);
} else {
time = def;
if (shouldPersist()) {
persistLong(time);
}
}

calendar.setTimeInMillis(time);

setSummary(getSummary());
}

@Override
public CharSequence getSummary() {
// Format time as per user's locale.
final DateFormat df =
SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT);
final Date time = calendar.getTime();
final String s = df.format(time);
return s;
}

public static boolean is24HourSystemDateSetting() {
final SimpleDateFormat f = (SimpleDateFormat)
SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT);
final String pattern = f.toPattern();
final boolean is24 = pattern.indexOf('H') >= 0;
return is24;
}
}