2006-09-22

Usability: Integration of file manager and File->Open dialog

In the light of the previous post I thought that copying the full path to a file to the clipboard and inserting it into File Open dialog running from another program it's a solution of a problem that could never happen.

I know two ways of starting work with a file. The first one is to open a required program, run File Open dialog and chose the file. The second is rely upon file manager, find and «run» the file from it. Many times I've met an opinion that that the possibility of the user interface to do the same thing in a few ways is not good. There is a point of view that files should not be opened at all...

In short, the best problem solution is the problem absence. But it is still not clear what to do with all this variety of programs and file managers.

On linux waving the magic wand LD_PRELOAD and replacing standard libraries without recompiling I can do everything I wish, I can dream:

Throwing off all pink elephants in yellow pants it seems to me that the most digestible solution is to display in File Open dialog folders, which are opened in my file managers.

As usual, they are about 10-15, so it is not difficult to pick up a necessary file. There is no need of copying and inserting anything. My attention is not spread on a whole lot of hard disk folders, everything I need is opened in my Nautilus. I'm concentrating on my task.


Update: No, it's not convenient, there is a need to open a file in a subfolder of an opened folder, so we've come to the beginning — the whole tree in File Open dialog.

2006-09-18

A brief HOWTO live under Linux

For some reason my interaction with Linux recently follows the algorithm:

  1. Extremely need some missing functionality in a necessary program

  2. For a long time I whimper, but stand it

  3. At last I read manuals suppose what if this feature exists and if does not how this program can be integrated with others

  4. Google for an external program implementing the necessary piece

  5. Put all together and get what I wanted.



For example, http client elisp implementation can not work through proxy with authorization, all who use it can not do it accordingly. It is possible to write in Live Journal using Emacs, but to post it is not. I googled, found a variant... and for a long time hardly tried to understand the implementation details so that to create a correct config. The result is here.

I constantly wanted to have a possibility of copying the full path to a current selection in a file manager - in Far it is done with Ctrl-Shift-F combination. After that in any program execute File->Open and insert this file name. Voila. I do not know how to get the same functionality in Nautilus but for mc I've found a console utility for working with clipboard and created an additional item of the user menu:


~/.mc/menu 1 Copy current location to clipboard
echo -n %d/%p | xclip -selection clipboard


It turned out that X server supports whole three kinds of clipboards...

2006-09-14

Automatization

At last, I had enough time to automate the site update. The problem is quite simple - there is a certain number of html pages, which should be changed at a local server from time to time, then wait for the confirmation of the maden changes and send it to the Internet. I would like to have one big button for that purpose, because I'm lazy to synchronize sites, get accustomed to an external tool... since the problem is exactly for make utility. The only trouble is that I've completely forgotten what does the magic symbols ($@ $^ and so on) mean.

But there is more easy way.

By chance I came across Martin Fowler article about the magic rake utility, used for building projects in ruby. More fifteen minutes of ruby functions googling and voila:

require 'rake' 
require 'find'

task :default => :copychanges
task :copychanges do
print "all done"
end

Find.find('src/') do |srcfile|
target = srcfile.sub(/^src/,'data')
changes = srcfile.sub(/^src/,'changes')
file target => srcfile do
if test(?f, srcfile)
cp srcfile, target cp srcfile, changes
else
mkdir_p target mkdir_p changes
end
end
task :copychanges => target
end



Make changes in the folder src/, store the copy in the folder data/, by rake command new changes are moved to the folder changes.

2006-09-13

how can i switch to Russian?

Hum... It seemed to me that immediately after the installation emacs detects system keyboard layout automatically, I thought that I made a mess in the config, but got the same result after the config deletion.

I'm deep in thought... to what key combinations bind the keyboard layouts switching.

2006-09-11

Usability: Undo

It's interesting, emacs has no concept of redo, a result of consecutive undos it's just a new state which in turn can be undone.

In the usual implementation of undo / redo when any changes are done after a series of undos all undo states are lost. From one side it is correct, and from the other side it is not. If the states were really wrong, the user will not remember about it and all is good, but if I drew a path in Photoshop, undo it, painted again... decided that at the first time I did it better and oops ...


Similarly, realization back/next in browsers bothered me until bookmarks support appeared.

True, in emacs also not everything is clean, to undo a part of a series of consecutive undos I need to type some useless keys combination to force emacs record the state. I would prefer undo sequence happens in the quasi mode: C-x C-u and while I hold Control and type then state fixing does not occur, if I make a mistake then I release Control the state is recorded and I can undo it again without changing my hands position.

2006-09-03

And why in E-macs moving to the beginning of a line is done by C-a?

Because A is the first letter in the alphabet?

2006-09-01

Refactoring in action

Gosh, I'm all beat up.

I started to hack up a small piece of an editor:
BOOL RichEditDlg::FillTemplateFromDocum(const CDocInfo &doc)
{
FINDTEXTEX ft; CMUFString str;

ft.chrg.cpMin=0; ft.chrg.cpMax=-1;

for(;;)
{
ft.lpstrText="{TAG";

long iPos=m_RichCtrl.FindText(0,&ft);
if(-1==iPos) break;

ft.lpstrText="}"; ft.chrg.cpMin=ft.chrgText.cpMin;

long iPos2=m_RichCtrl.FindText(0,&ft);
if(-1==iPos2) continue;

ft.chrgText.cpMin=iPos;

m_RichCtrl.SetSel(ft.chrgText);

str=m_RichCtrl.GetSelText();
str.Replace("{TAG",""); str.Replace("}","");

int iFld=atoi(str), iVol=1;
if(0==iFld) continue;

int iPos3=str.FindOneOf(".,");
if(iPos3>0) iVol=atoi(str.Mid(iPos3+1));

CString sTmp;
iPos3=str.Find("_");

if(iPos3>0) sTmp=str.Mid(iPos3+1); else str.Empty();

CDocInfo::stFieldInfo fi; doc.GetFieldInfo(iFld,fi);
str=doc.GetValue(iFld,iVol);

if(sTmp=="PROP") // Value in lower case
{
if(0==fi.sFieldType.Left(5).CompareNoCase("NUMBER"))

str=str.FormatSum();
else if(0==fi.sFieldType.Left(4).CompareNoCase("DATE"))

str=str.DateToScript();
else if(0==fi.sFieldType.Left(4).CompareNoCase("CHAR"))

{
CString sTmp1=fi.sFieldName; sTmp.MakeLower();

if(sTmp1.Find("address"))
str=AddrToTxt(str);

}
}
m_RichCtrl.ReplaceSel(str);
}

}


Someone can argue, but IMHO the code terribly smells.
First of all, sort out nameless constants.
const CHARRANGE FULLTEXT = {0,-1};
ft.chrg = FULLTEXT;

Go further, get rid of the implicit modifying operations.
Starting from the passing and returning to the separate function the whole structure FULLTEXT retain only the necessary and get:
const CHARRANGE NOTFOUND = {-1,-1};

CHARRANGE lookup(CRichEditCtrl &richEdit, const CHARRANGE &searchRange, const char* lookupString) {

FINDTEXTEX findData;
findData.chrg = searchRange;
findData.lpstrText = const_cast<char*>(lookupString);

if(richEdit.FindText(FR_MATCHCASE,&findData))
return findData.chrgText;

return NOTFOUND;
}

bool operator==(const CHARRANGE &cr1, const CHARRANGE &cr2) {

return cr1.cpMin == cr2.cpMin && cr1.cpMax == cr2.cpMax;

}

Fix a small piece
/*
ft.lpstrText="{TAG";
long iPos=m_RichCtrl.FindText(0,&ft);
if(-1==iPos) break;*/

result = lookup(m_RichCtrl,FULLTEXT,"{TAG");

if(NOTFOUND == result)
break;
long iPos = result.cpMin;

The next piece
/*
ft.lpstrText="}"; ft.chrg.cpMin=ft.chrgText.cpMin;
long iPos2=m_RichCtrl.FindText(0,&ft);
if(-1==iPos2) continue;
ft.chrgText.cpMin=iPos;
m_RichCtrl.SetSel(ft.chrgText);*/

CHARRANGE lookform = {result.cpMin,-1};

result = lookup(m_RichCtrl,lookform,"}");
if(NOTFOUND == result)

continue;

CHARRANGE selectionRange = {iPos,result.cpMax};

m_RichCtrl.SetSel(selectionRange);

A little bit long and terrifying but now it is clear what is really happening here.

And one:
CHARRANGE startTag = lookup(m_RichCtrl,FULLTEXT,"{TAG");

if(NOTFOUND == startTag)
break;
long iPos = startTag.cpMin;


CHARRANGE lookform = {startTag.cpMin,-1};
CHARRANGE endTag = lookup(m_RichCtrl,lookform,"}");

if(NOTFOUND == endTag)
continue;

CHARRANGE selectionRange = {startTag.cpMin,endTag.cpMax};

m_RichCtrl.SetSel(selectionRange);


And two:
CHARRANGE lookupFrom(CRichEditCtrl &richEdit, int startPos, const char* lookupString);

CHARRANGE endTag = lookupFrom(m_RichCtrl,startTag.cpMin,"}");

if(NOTFOUND == endTag)
continue;

CHARRANGE selectionRange = {startTag.cpMin,endTag.cpMax};

m_RichCtrl.SetSel(selectionRange);


The next piece.
/*str=m_RichCtrl.GetSelText();
str.Replace("{TAG",""); str.Replace("}","");
int iFld=atoi(str), iVol=1;*/

CString getTagContent(CRichEditCtrl &richEdit,const CHARRANGE &startTag, const CHARRANGE &endTag) {

CHARRANGE selectionRange = {startTag.cpMin,endTag.cpMax};

richEdit.SetSel(selectionRange);

CString tagContent=richEdit.GetSelText();


tagContent.Replace("{TAG","");
tagContent.Replace("}","");

return tagContent;
}


str = getTagContent(m_RichCtrl,startTag,endTag);

int iFld=atoi(str);
int iVol=1;


I'm tired and doing careless work - commenting.
/*
int iPos3=str.FindOneOf(".,");
if(iPos3>0) iVol=atoi(str.Mid(iPos3+1));
CString sTmp;
iPos3=str.Find("_");
if(iPos3>0) sTmp=str.Mid(iPos3+1); else str.Empty();
*/

//Define additional code
int iPos3=tagContent.FindOneOf(".,");
if(iPos3>0) iVol=atoi(tagContent.Mid(iPos3+1));

//Define suffix
CString sTmp;
iPos3=tagContent.Find("_");

if(iPos3>0)
sTmp=tagContent.Mid(iPos3+1);

else
tagContent.Empty();


suffix proceeding goes immediately to the end
void replaceTag(
CRichEditCtrl &richEdit,
const CHARRANGE &startTag,
const CHARRANGE &endTag,

const char* newContent) {
CHARRANGE selectionRange = {startTag.cpMin,endTag.cpMax};

richEdit.SetSel(selectionRange);
richEdit.ReplaceSel(newContent);

}

replaceTag(startTag,endTag,str);



God damn my laziness, but to write tests for code processing 10 global structures is problematical, so I test it manually.

Before the very running I've noticed the error, fix it
CHARRANGE startTag,endTag;
for(int curPos = 0;;curPos=endTag.cpMax)


It works. Before further improvements can be done we should generalize work with RichEdit
class TextRange {
public:
TextRange()
:startPos(-1),endPos(-1) {};

TextRange(int start, int end)
:startPos(start),endPos(end){}

int startPos;
int endPos;

friend bool operator==(const TextRange&, const TextRange&);

static const TextRange NOTFOUND;
static const TextRange FULLTEXT;

};


class ISourceContainer
{
public:
ISourceContainer();

virtual ~ISourceContainer();

virtual TextRange lookupFrom(
int startPos,
const char* lookupString) = 0;

virtual std::string getTagContent(

const TextRange &startTag,
const TextRange &endTag) = 0;

virtual void replaceTag(

const TextRange &startTag,
const TextRange &endTag,
const char* newContent) = 0;

};

Inherit from it a class with the constructor
RichEditSourceContainder(CRichEditCtrl &richEdit)
and transfer there our functions realization.

Ensure that everything works do one more extract method and get:
RichEditSourceContainer sourceContainer(m_RichCtrl);

TextRange startTag,endTag;
for(int curPos = 0;;curPos=endTag.endPos)

{
startTag = sourceContainer.lookupFrom(curPos,"{MIF");

if(TextRange::NOTFOUND == startTag)
break;
endTag = sourceContainer.lookupFrom(startTag.startPos,"}");

if(TextRange::NOTFOUND == endTag)
break;

CString tagContent = sourceContainer.getTagContent(startTag,endTag).c_str();


//define field number
int iFld=atoi(tagContent);
if(0==iFld)
continue;

//define nesting
int iVol=1;
int iPos3=tagContent.FindOneOf(".,");

if(iPos3>0)
iVol=atoi(tagContent.Mid(iPos3+1));

//define suffix
CString sTmp;
iPos3=tagContent.Find("_");

if(iPos3>0)
sTmp=tagContent.Mid(iPos3+1);
else
tagContent.Empty();

CString fieldValue=doc.GetValue(iFld,iVol);
if(sTmp=="PROP") // Value in upper case

fieldValue = getFieldTextValue(doc,iFld,iVol);
//replace tag with field value

sourceContainer.replaceTag(startTag,endTag,fieldValue);
}



That's all, I'm tired, it's Friday.

On the next stage I'll move this piece out from the dialog to the model... Gosh!