Macro to swap selections - macros

As an author, I frequently have to swap two words, phrases or sentences. I do that by dragging and dropping or by using the clipboard history, or by retyping, all of which are cumbersome and prone to mistakes.
Is there a command or macro to automatically swap two selections?
Example: after selecting 'short' and 'simple' in the sentence 'This is a short and simple example' and swapping them, the sentence would become 'This is a simple and short example'
For simple word swapping, it would be handy if such a function would default to selecting the words with a cursor (empty selection) in them.
Example: place two cursors in the above sentence, one in the word 'simple' and one in 'short', and they'll be automatically selected and reversed.
For swapping a word and a phrase, it would be handy if I could select the word by Ctrl-clicking and the phrase by dragging to have it automatically selected (that is, a combination of 1 and 2).
More generally, I would like to be able to invert any number of selections of any length anywhere in a document.
Simple example: the text 'a b c d e f g' with the a, the c, the e and the g selected becomes 'g b e d c f a'.
Obviously swapping two selections is a special case of reversing any number of selections.

Here is a JavaScript macro for EmEditor to swap multiple selections or words at the cursor positions.
Redraw = false; // optimize for speed
CombineHistory = true; // combine Undo
nMax = document.selection.Count; // retrieve the number of selections
ax = Array( nMax );
ax2 = Array( nMax );
ay = Array( nMax );
ay2 = Array( nMax );
as = Array( nMax );
for( i = 0; i < nMax; ++i ) { // retrieve position of each selection
ax[i] = document.selection.GetTopPointX( eePosLogical, i + 1 );
ax2[i] = document.selection.GetBottomPointX( eePosLogical, i + 1 );
ay[i] = document.selection.GetTopPointY( eePosLogical, i + 1 );
ay2[i] = document.selection.GetBottomPointY( eePosLogical, i + 1 );
}
for( i = 0; i < nMax; ++i ) { // retrieve word text and positions
if( ax[i] == ax2[i] && ay[i] == ay2[i] ) {
document.selection.SetActivePoint( eePosLogical, ax[i], ay[i] ); // set cursor position
document.selection.SelectWord(); // select a word
ax[i] = document.selection.GetTopPointX( eePosLogical ); // retrieve selection position
ax2[i] = document.selection.GetBottomPointX( eePosLogical );
as[i] = document.selection.Text; // retrieve selected text
}
else {
document.selection.SetActivePoint( eePosLogical, ax[i], ay[i] );
document.selection.SetActivePoint( eePosLogical, ax2[i], ay2[i], true );
as[i] = document.selection.Text; // retrieve selected text
}
}
for( i = nMax - 1; i >= 0; --i ) { // set a selection from bottom to top
document.selection.SetActivePoint( eePosLogical, ax[i], ay[i] );
document.selection.SetActivePoint( eePosLogical, ax2[i], ay2[i], true );
document.selection.Text = as[nMax - i - 1]; // set new text
}
To run this, save this code as, for instance, Macro.jsee, and then select this file from Select... in the Macros menu. Finally, select Run Macro.jsee in the Macros menu while multiple selections are made.

Related

Is there a better way to calculate the moving sum of a list in flutter

Is there a better way to calculate a moving sum of a list?
List<double?> rollingSum({int window = 3, List data = const []}) {
List<double?> sum = [];
int i = 0;
int maxLength = data.length - window + 1;
while (i < maxLength) {
List tmpData = data.getRange(i, i + window).toList();
double tmpSum = tmpData.reduce((a, b) => a + b);
sum.add(tmpSum);
i++;
}
// filling the first n values with null
i = 0;
while (i < window - 1) {
sum.insert(0, null);
i++;
}
return sum;
}
Well, the code is already clean for what you need. Maybe just some improvements like:
Use a for loop
You can use the method sublist which creates a "view" of a list, which is more efficient
To insert some values in the left/right of a list, there is a specific Dart method called padLeft, where you specify the lenght of the list which you want it to become (first parameter), then the value you want to use to fill it (second parameter). For example, if you have an array of N elements, and you want to fill it with X "null"s to the left, use padLeft(N+X, null).
List<double?> rollingSum({int window = 3, List data = const []}) {
List<double?> sum = [];
for (int i = 0; i < data.length - window + 1; i++) {
List tmpData = data.sublist(i, i + window);
double tmpSum = tmpData.reduce((a, b) => a + b);
sum.add(tmpSum);
}
sum.padLeft(window - 1, null);
return sum;
}
if I understand your problem correctly you can just calculate the window one time and in one loop you can for each iteration you can add the current element to the sum and subtract i - (window - 1)
so for an input like this
data = [1,2,3,4,5,6]
window = 3
the below code will result in [6,9,12,15]
int sum = 0;
List<double> res = [];
for (int i = 0;i<data.length;i++) {
sum += data[i];
if (i < window - 1) {
continue;
}
res.add(sum);
sum -= data[i - (window - 1)]; // remove element that got out of the window size
}
this way you won't have to use getRange nor sublist nor reduce as all of those are expensive functions in terms of time and space complexity

How to exclude a column when using getAllColumns property in ag-grid

I have a function that counts and hides the number of columns that does not fit the screen. I want to exclude a column when resizing and hiding the columns. Here is what I have.
let ctrOfColumns = this.gridOptionsValue.columnApi.getAllColumns();
this returns the columns that i have. I want to exclude a specific column which has a colId of 'toBeExcludedId' so that It won't be included in the hiding of columns algo.
Here is my algo in hiding of the columns
let gridWidthOfMyTable = $('#idOfMyGrid').outerWidth();
let columnsToBeShown = [];
let columnsToBeHidden = [];
let totalWidthOfColumn = 0;
for(let x = 0 ; x < ctrOfColumns.length; x ++){
const singleColumn = ctrOfColumns[x];
totalWidthOfColumn += singleColumn.getMinWidth();
if (totalWidthOfColumn > gridWidthOfMyTable) {
columnsToBeHidden.push(singleColumn);
} else {
columnsToBeShown.push(singleColumn);
}
}
this.gridOptionsValue.columnApi.setColumnsVisible(columnsToBeShown, true);
this.gridOptionsValue.columnApi.setColumnsVisible(columnsToBeHidden, false);
There is no need to loop through all the values in your array. You can just use chaining and apply a filter directly to getAllColumns(), like this:
let ctrOfColumns = this.gridOptionsValue
.columnApi
.getAllColumns()
.filter((column) => column.colId !== 'toBeExcludedId');

Bokeh: updating widget after initial creation

I'm trying to create a tool that will allow a user to select different parameters for plotting model data. I can generate the widgets initially, but I'd like to update one of the widgets based on a possible selection from the user.
Bokeh page
In this example, I've defaulted the value "Phase" to "Water" and "Effective Radius" to a list of values that correspond with water-only. If the user selects "Ice" from "Phase", I'd like the widget to update from a different list of values. Here is the relevant code:
from bokeh.models.widgets import (Slider, Button, CheckboxGroup,
RadioButtonGroup, RadioGroup, Dropdown, Select)
# Selector for phase.
#--------------------
phs = [ "Water", "Ice", "Clear" ]
phs_select1 = Select( title="Cloud Phase", value=phs[0], options=phs,
width=150 )
phs_select2 = Select( title="Cloud Phase", value=phs[0], options=phs,
width=150 )
def update_phs( flag ):
if flag == 0:
fname[flag,3] = phs_select1.value.lower()
update_de_list( )
de_select1.update()
if flag ==1:
fname[flag,3] = phs_select2.value.lower()
update_de_list( )
phs_select1.on_change( 'value', lambda attr, new, old: update_phs(0) )
phs_select2.on_change( 'value', lambda attr, new, old: update_phs(1) )
# Selector for effective diameter.
#----------------------------
de = [np.str(x) for x in (8, 20, 32, 50)]
def update_de_list():
# Add something here to update the widget?
?????????????????????????
if phs_select1.value.lower() == "water":
de = [np.str(x) for x in (8, 20, 32, 50)]
elif phs_select1.value.lower() == "ice":
de = [np.str(x) for x in (21.86, 46.34, 115.32)]
????????????????????????????????????
?
? Once user has selected a different phase, I need to update
? "Effective Diameter field.
?
?????????????????????????
de_select1= Select( title="Effective Diameter", value=de[0], options=de,
width=150 )
de_select2 = Select( title="Effective Diameter", value=de[0], options=de,
width=150 )
def update_de( flag ):
if flag == 0:
fname[flag,5] = "de"+de_select1.value
if flag == 1:
fname[flag,5] = "de"+de_select2.value
de_select1.on_change( 'value', lambda attr, new, old: update_de(0) )
de_select2.on_change( 'value', lambda attr, new, old: update_de(1) )
# Layout the widgets for the first file
lft_pnl = [srf_select1,igbp_select1,tau_select1,ws_select1,aod_select1]
rgt_pnl = [ prf_select1, phs_select1, de_select1, cld_select1 ]
lft_col = WidgetBox( *lft_pnl, sizing_mode="fixed", width=150)
rgt_col = WidgetBox( *rgt_pnl, sizing_mode="fixed", width=150)
# Layout the widgets for the first file
lft_pnl = [srf_select2,igbp_select2,tau_select2,ws_select2,aod_select2]
rgt_pnl = [ prf_select2, phs_select2, de_select2, cld_select2 ]
lft_col2 = WidgetBox( *lft_pnl, sizing_mode="fixed", width=150)
rgt_col2 = WidgetBox( *rgt_pnl, sizing_mode="fixed", width=150)
pnl_layout = layout([[lft_col, rgt_col],[lft_col2,rgt_col2],[plt_button]])
l = layout(children=[[grid,pnl_layout]])
curdoc().add_root( l )
Some bits of code are missing in your example, so I hope I understood your problem correctly :). As far as I can tell, you would like to change the options in the "Effective Diameter" field when the user changes the selection in the "Cloud Phase" field. I think to achieve what you would like to, you could just replace the following line inside the update_de_list() callback:
de = [np.str(x) for x in (21.86, 46.34, 115.32)]
with:
de_select1.options = [np.str(x) for x in (21.86, 46.34, 115.32)]
de_select2.options = [np.str(x) for x in (21.86, 46.34, 115.32)]

Change sheet cells color onEdit

I figured out how to compare dates in Google Sheets but when I try to enter more dates for some reason all the cells that were green and red become all red. Also how can I make two cells red if only one cell has a date?
Example: In cell D18 the Due date is 4-18-2014 and in cell E18 the cell is blank. I want to make both cells red so I would know that I should find out why is that cell red.
This is the code I have so far:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Copy of Project Sheet 1');
var values1Rule1 = s.getRange('E2:E1000').getValues();
var values2Rule1 = s.getRange('D2:D1000').getValues();
var range3Rule1 = s.getRange('D2:E2');
var color1 = 'Red';
var color2 = 'Green';
for (var row in values1Rule1) {
for (var col in values1Rule1[row]) {
if (values1Rule1[row][col] > values2Rule1[row][col]) s.getRange(s.getRange('D2').offset(row, col, 1, 2).getA1Notation()).setBackgroundColor(color1);
else if (values1Rule1[row][col] < values2Rule1[row][col]) s.getRange(s.getRange('D2').offset(row, col, 1, 2).getA1Notation()).setBackgroundColor(color2);
else s.getRange(s.getRange('D2').offset(row, col, 1, 2).getA1Notation()).setBackgroundColor('white'); }}
};
All you need to do is add this condition as an OR clause in your red condition, e.g.
if (values1Rule1[row][col] > values2Rule1[row][col] || values1Rule1[row][col] === '')
But there's lots of "minor" problems with your code. First of all, you're doing way too many API calls unnecessarily. This is a big performance issue. For example, when you offset, you already have the new range, there's no need to getA1Notation then get the range again, you could do:
s.getRange('D2').offset(row, col, 1, 2).setBackgroundColor(color1);
But that's still two calls, getting D2, then offseting. You could get the desired range at once:
s.getRange(row+1, 4, 1, 2).setBackgroundColor(color1);
I'd go even further and build a matrix of colors and set it all at once after the loop:
s.getRange('D2:E1000').setBackgroundColors(colors);
But even better, inside an onEdit you should only work on what has just being edited, instead of triggering a full recalculation of your colors because the user edited something on another column or another sheet entirely.
I think your code should be something like this:
function onEdit(e) {
var ss = e.source;
var s = ss.getActiveSheet();
if( s.getName() !== 'Copy of Project Sheet 1' ) return; //only interested in one sheet
var r = s.getActiveRange();
var c = r.getColumn();
if( c !== 4 && c !== 5 ) return; //only interested in changes on columns D or E
r = r.offset(0, c === 4 ? 0 : -1, 1, 2);
var v = r.getValues()[0];
r.setBackgroundColor(v[1] === '' || v[1] > v[0] ? 'red' : v[1] < v[0] ? 'green' : 'white');
}
--edit
You can not run this function manually directly, because it needs a parameter that is passed only when it runs automatically. But you can emulate it with a test function, like this:
function testEdit() { onEdit({source:SpreadsheetApp.getActive()}); }

fpdf multicell issue

how we display fpdf multicell in equal heights having different amount of content
Great question.
I solved it by going through each cell of data in your row that will be multicelled and determine the largest height of all these cells. This happens before you create your first cell in the row and it becomes the new height of your row when you actually go to render it.
Here's some steps to achieve this for just one cell, but you'll need to do it for every multicell:
This assumes a $row of data with a String attribute called description.
First, get cell content and set the column width for the cell.
$description = $row['desciption']; // MultiCell (multi-line) content.
$column_width = 50;
Get the width of the description String by using the GetStringWidth() function in FPDF:
$total_string_width = $pdf->GetStringWidth($description);
Determine the number of lines this cell will be:
$number_of_lines = $total_string_width / ($column_width - 1);
$number_of_lines = ceil( $number_of_lines ); // Round it up.
I subtracted 1 from the $column_width as a kind of cell padding. It produced better results.
Determine the height of the resulting multi-line cell:
$line_height = 5; // Whatever your line height is.
$height_of_cell = $number_of_lines * $line_height;
$height_of_cell = ceil( $height_of_cell ); // Round it up.
Repeat this methodology for any other MultiCell cells, setting the $row_height to the largest $height_of_cell.
Finally, render your row using the $row_height for your row's height.
Dance.
I was also getting same issue when i have to put height equal to three lines evenif i have content of half or 1 and half line, i solved this by checking if in a string no of elements are less than no of possible letters in a line it is 60. If no of letters are less or equal to one line then ln() is called for two empty lines and if no of words are equal to or less than 2 line letters then one ln() is called.
My code is here:
$line_width = 60; // Line width (approx) in mm
if($pdf->GetStringWidth($msg1) < $line_width)
{
$pdf->MultiCell(75, 8, $msg1,' ', 'L');
$pdf->ln(3);
$pdf->ln(3.1);
}
else{
$pdf->MultiCell(76, 4, $msg1,' ', 'L');
$pdf->ln(3.1);
}
Ok I've done the recursive version. Hope this helps even more to the cause!
Take in account these variables:
$columnLabels: Labels for each column, so you'll store data behind each column)
$alturasFilas: Array in which you store the max height for each row.
//Calculate max height for each column for each row and store the value in //////an array
$height_of_cell = 0;
$alturasFilas = array();
foreach ( $data as $dataRow ) {
for ( $i=0; $i<count($columnLabels); $i++ ) {
$variable = $dataRow[$i];
$total_string_width = $pdf->GetStringWidth($variable);
$number_of_lines = $total_string_width / ($columnSizeWidth[$i] - 1);
$number_of_lines = ceil( $number_of_lines ); // Redondeo.
$line_height = 8; // Altura de fuente.
$height_of_cellAux = $number_of_lines * $line_height;
$height_of_cellAux = ceil( $height_of_cellAux );
if($height_of_cellAux > $height_of_cell){
$height_of_cell = $height_of_cellAux;
}
}
array_push($alturasFilas, $height_of_cell);
$height_of_cell = 0;
}
//--END--
Enjoy!
First, it's not the question to get height of Multicell. (to #Matias, #Josh Pinter)
I modified answer of #Balram Singh to use this code for more than 2 lines.
and I added new lines (\n) to lock up height of Multicell.
like..
$cell_width = 92; // Multicell width in mm
$max_line_number = 4; // Maximum line number of Multicell as your wish
$string_width = $pdf->GetStringWidth($data);
$line_number = ceil($string_width / $cell_width);
for($i=0; $i<$max_line_number-$line_number; $i++){
$data.="\n ";
}
Of course you have to assign SetFont() before use GetStringWidth().
My approach was pretty simple. You already need to override the header() and footer() in the fpdf class.
So i just added a function MultiCellLines($w, $h, $txt, $border=0, $align='J', $fill=false).
That's a very simple copy of the original MultiCell. But it won't output anything, just return the number of lines.
Multiply the lines with your line-height and you're safe ;)
Download for my code is here.
Just rename it back to ".php" or whatever you like. Have fun. It works definitely.
Mac
My personal solution to reduce the font size and line spacing according to a preset box:
// set box size and default font size
$textWidth = 100;
$textHeight = 100;
$fontsize = 12;
// if you get text from html div (using jquery and ajax) you must replace every <br> in a new line
$desc = utf8_decode(str_replace('<br>',chr(10),strip_tags($_POST['textarea'],'<br>')));
// count newline set in $desc variable
$countnl = substr_count($desc, "\n");
// Create a loop to reduce the font according to the contents
while($pdf->GetStringWidth($desc) > ($textWidth * (($textHeight-$fontsize*0.5*$countnl) / ($fontsize*0.5)))){
$fontsize--;
$pdf->SetFont('Arial','', $fontsize);
}
// print multicell and set line spacing
$pdf->MultiCell($textWidth, ($fontsize*0.5), "$desc", 0, 'L');
That's all!
A better solution would be to use your own word wrap function, as FPDF will not cut words, so you do not have to rely on the MultiCell line break.
I have wrote a method that checks for every substring of the text if the getStringWidth of FPDF is bigger than the column width, and splits accordingly.
This is great if you care more about a good looking PDF layout, than performance.
public function wordWrapMultiCell($text, $cellWidth = 80) {
$explode = explode("\n", $text);
array_walk($explode, 'trim');
$lines = [];
foreach($explode as $split) {
$sub = $split;
$char = 1;
while($char <= strlen($sub)) {
$substr = substr($sub, 0, $char);
if($this->pdf->getStringWidth($substr) >= $cellWidth - 1) { // -1 for better getStringWidth calculating
$pos = strrpos($substr, " ");
$lines[] = substr($sub, 0, ($pos !== FALSE ? $pos : $char)).($pos === FALSE ? '-' : '');
if($pos !== FALSE) { //if $pos returns FALSE, substr has no whitespace, so split word on current position
$char = $pos + 1;
$len = $char;
}
$sub = ltrim(substr($sub, $char));
$char = 0;
}
$char++;
}
if(!empty($sub)) {
$lines[] = $sub;
}
}
return $lines;
}
This returns an array of text lines, which you could merge by using implode/join:
join("\r\n", $lines);
And what it was used for in the first place, get the line height:
$lineHeight = count($lines) * $multiCellLineHeight;
This only works for strings with a space character, no white spacing like tabs. You could replace strpos with a regexp function then.