2011年11月26日 星期六

Developer plugin 的願景

它的功能是Developer Documentation Access,如此。
但原本的功能卻壞掉了,因為該module預設的搜尋資料夾/Developer/ADC Reference Library/Documentation/Cocoa/Reference/已不復存在。


現在我把常用的查詢設定為web search,諸如apple dev、cplusplus等。
但其實os x core 10.6的文件(含cocoa及其他許多文件)其實用Xcode下載,在本機上就有一份了,在沒有網路的情形下也可使用。要如何讓存取文件變得更簡單呢?
想把這個工作統一到Alfred或QS的介面上呢。
另外常用的Unix util 的 man也是呢。

2011年11月19日 星期六

Alfred與QS的輸入介面

Alfred在中文上比QS好用的一個原因是,它的輸入介面作為一個"text editor"功能是很完備的。
可以反白、刪除、複製、剪下、貼上,與其他mac應用程式的使用經驗相符。
可以這麼說,它就是一個加上即時搜尋的文字輸入框。

而QS的輸入介面,概念上文字與搜尋到的物件是一體的。
快速是QS的特色,打錯就delete就清空重來,而連按兩下delete將pane1清空更是簡潔有力。

2011年11月10日 星期四

bug comes!

在Preference->Command下,若Spacebar behavior設定成Normal,
在pane1隨便打個空白就爆了。我現在是設成Switch to Text Mode。
哭哭。

2011年11月9日 星期三

筆記一下修改的地方

修改完成了。現在QS與中文輸入相容了!
來記錄一下修改的地方吧!

QSObjectView.m
在setSearchString:中加了一行

NSLog(@"setSearchString: %@", newSearchString);
作為debug用,不影響功能。

QSSearchObjectView.h
拿掉
//- (BOOL)handleSlashEvent:(NSEvent *)theEvent;
//- (BOOL)handleTildeEvent:(NSEvent *)theEvent;
這兩個method,取而代之的是
- (BOOL)handleSlash:(NSString *)aString;
- (BOOL)handleTilde:(NSString *)aString;
另外新增
- (void)processTextCommand:(NSString*)aString;
- (BOOL)handleBoundTextKey:(NSString *)aString;
兩個method。

QSSearchObjectView.h
1. 註解掉performKeyEquivalent:的實作。(目前先不註解,改成印出debug訊息)
2. setMarkedText: selectedRange:只留下空殼,內容都註解掉。(這應該是中文字一定要等打完才看得到的原因)
3. insertText:一開始檢查aString的型別,目的是處理NSAttributedString。後面會call processTextCommand:這個method。
4. 新增processTextCommand:與handleBoundTextKey:兩個method。前者處理slash與tilde鍵,後者處理定義在dict中的指令。最重要的改變是,這些指令從在keyDown就執行延後到insertText:時才執行了。

結果:
handleBoundKey只有在performKeyEquivalent:被call時才有可能執行。


已知bug:
1. 在pane1打「修改」,這時QS找不到東西,但仍可以按tab進入pane2,選Dictionary的話,會卡住。解法是用滑鼠點選pane1,並按兩下delete。
2. 不知名情況下,QS會停住,Xcode會花很久蒐集stack frame。




實驗!

實驗:把- (BOOL)performKeyEquivalent:拿掉,QS還能正常運作嗎?
動機:在QSSearchObjectView中,有兩個method會call handleBoundKey:
就是keyDown與performKeyEquivalent:。
因為現在要把keyDown的部分功能移到insertText:去了,擔心在某些case會被低階的performKeyEquivalent:攔截而去執行handleBoundKey:,故先將其註解掉。

實驗:handleBoundKey處理的key
結果:確定會處理,.;'這四個key,而/`不會。

目前/與`是由獨立的handleSlashEvent:與handleTildeEvent:處理。
我想應該都可以整合到handleRepeaterEvent:吧!

快捷鍵問題

中文輸入解決了。接下來要處理快捷鍵問題。
中文與英文的keyboard layout不同,其中ㄝㄡㄥㄤ四個鍵在英文鍵盤都只是標點符號,加上輸入法「一聲」需要的空白鍵共五個鍵,在QS中被拿來當做指令使用。
只要讓這五個鍵「復權」,就能讓QS真的變成中文輸入相容了。

以下是我尚未了解的概念:
keyboard actions
action method
key equivalent


keyboard actions,是NSResponder定義好的。subclass可覆寫之。
key equivalent比key interface control還要早被攔截。

看來必須在insertText:處理囉~

QSSearchObjectView的兩個doCommandBySelector:

我突然懂了為什麼QSSearchObjectView同時需要
doCommandBySelector:

textView:doCommandBySelector:
了!
前者是一般用途,而後者是在pane1或pane3進入文字編輯模式時使用的。
例如打一些字->large type,
或者google taiwan->search->打一些字
這種時候會用到後者。

錯誤訊息


把專案抓下來後編譯,觀察console會發現,
在輸入中文字時會噴錯誤訊息
[NSConcreteMutableAttributedString purifiedString]: unrecognized selector sent to instance 0x14ac8b20
原因是NSMutableAttributedString不能回應QS對NSString擴充的purifiedString method所致,案發現場是
 4 -[QSSearchObjectView setMarkedText:selectedRange:] (in QSInterface) (QSSearchObjectView.m:1539)
 把purifiedString拿掉後重新編譯,會噴

-[NSConcreteMutableAttributedString _encodingCantBeStoredInEightBitCFString]: unrecognized selector sent to instance 0x151170e0
原因是編碼不能被儲存在8-bitCFString,案發現場是
 7 -[QSSearchObjectView setMarkedText:selectedRange:] (in QSInterface) (QSSearchObjectView.m:1541) 


launcher隨筆


一個好的launcher究竟應該要具備什麼功能呢?

檢視一下我最常做的事,無非是...

  • 打開一個應用程式
  • 關掉一個應用程式
  • 尋找並打開一個檔案
  • 找到某個檔案所在的資料夾
  • 播放某首歌(如果我記得歌名的話)
  • 打開某個網頁(高鐵時刻表, FB, etc.)
  • google搜尋
  • 中文wiki搜尋
  • 英文wiki搜尋


launcher迷人的地方在於它讓你直接告訴電腦「請幫我做這個!」
單純,乾淨,沒有任何多餘的東西。
GUI世界最大的缺點就是過於花俏華麗,太多不相干的東西不停在吸引、分散人的注意力,不堪其擾。
電腦該是工具而不是玩具,工具幫助人提昇生產力;而玩具吸引你的注意,讓你流連忘返。
我不會看到別的應用程式而手癢去開它,也不會中途看到其他網頁。
我要這個,就給我這個。


那麼Launcher本身也必須單純才行,QS實在有些...太花俏了。
五花八門的plugin讓人愛不釋手呢。

這些額外功能包括計算機(Alfred也有)、文字操作、影像縮放、影像轉檔、壓縮等。
好用的包括在終端機打開某檔案的所在資料夾、把檔案email給某人(結合Mail與通訊錄)等。
冷僻的包括更改桌布、取得dropbox的public link等。

講難聽是奇技淫巧,講的好聽就是用起來很爽。
也許是我還不夠習慣比現在更便捷的操作?或許等我上手了,會覺得本來就應該要這麼方便吧。


接下來想說一些QS跟Alfred之間的差異。
Alfred在搜尋時不會顯示太多雜七雜八的東西,但QS會。雖然你想要的東西一定在前幾名,但不曉得底下那些亂糟糟的東西到底是從哪兒來的...有礙觀瞻!

QS是先編一份catalog,再去這裡搜。好處是快速,但不同步。好在按下Command-R可以重新製作catalog,如果沒有自定過於龐大的路徑,這很快就可以完成。
而Alfred每次搜尋都會去翻Hard Disk,感覺有點傷硬碟?好處是同步,壞處是稍慢,需要等它一下下。

最後,Alfred讓我驚艷的是,它預設會搜尋Things待辦事項,甚至連Mail的信都能搜尋。Mail的信可不是照檔名就搜的到的阿,真是太威了!
而且這是你真的在用的東西。
有次經驗讓我印象至深。那時在找去年服務隊的心得,在Mail的搜尋欄怎麼搜都搜不到,急煞我也。但Alfred卻像時光機一般,挖出深埋信箱的回憶。連雄鷹之歌、男女子大學部歌等,只要曾附件在信中,或者信的標題有提到的,通通挖的出來。實在驚人。

真希望QS可以快點支援中文哪。

2011年11月8日 星期二

重頭戲


NSTextInputClient Protocol Reference

Adopted by
Framework
/System/Library/Frameworks/AppKit.framework
Availability
Available in Mac OS X v10.5 and later.
Declared in
NSTextInputClient.h

Overview

The NSTextInputClient protocol defines the methods that Cocoa text views must implement in order to interact properly with the text input management system. To create another text view class, you can either subclass NSTextView (and not NSText, for historical reasons), or subclass NSView and implement the NSTextInputClient protocol

setMarkedText:selectedRange:replacementRange:

Replaces a specified range in the receiver’s text storage with the given string and sets the selection. (required)
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
Parameters
aString
The string to insert. Can be either an NSString or NSAttributedString instance.
selectedRange
The range to set as the selection, computed from the beginning of the inserted string.
replacementRange
The range to replace, computed from the beginning of the marked text.
Discussion
If there is no marked text, the current selection is replaced. If there is no selection, the string is inserted at the insertion point.
When aString is an NSString object, the receiver is expected to render the marked text with distinguishing appearance (for example,NSTextView renders with markedTextAttributes).
Availability
  • Available in Mac OS X v10.5 and later.
Declared In
NSTextInputClient.h


Binding Keystrokes

doCommandBySelector:

Invokes the action specified by the given selector. (required)
- (void)doCommandBySelector:(SEL)aSelector
Parameters
aSelector
The selector to invoke.
Discussion
If aSelector cannot be invoked, then doCommandBySelector: should not pass this message up the responder chain. NSResponder also implements this method, and it does forward uninvokable commands up the responder chain, but a text view should not. A text view implementing the NSTextInputClient protocol inherits from NSView, which inherits from NSResponder, so your implementation of this method will override the one in NSResponder. It should not call super.
Availability
  • Available in Mac OS X v10.5 and later.
See Also
Declared In
NSTextInputClient.h


2011年11月7日 星期一

筆記幾篇重要的文章

介紹selector與target-action機制的文章
http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/Chapters/ocSelectors.html

關於key-binding的
http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/TextEditing/Concepts/KeyBindings.html#//apple_ref/doc/uid/TP40008900-BAJCDFFI
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/EventOverview/TextDefaultsBindings/TextDefaultsBindings.html
http://www.hcs.harvard.edu/~jrus/site/cocoa-text.html
http://www.hcs.harvard.edu/~jrus/Site/System%20Bindings.html

QSSearchObjectView實作NSTextViewDelegate的用意

發現,在pane1輸入"."進入文字模式時,
凡按下delete、command-C、tab,都會在console印出"select a command~"
而這是寫在textView:doCommandBySelector:中的除錯訊息。

我的猜想是,進入文字模式時會產生一個NSTextView,而QSSearchObjectView就是它的delegate,有事請找我的感覺。

按下"."會發生什麼事呢?
在initialize這個method,會初始化一個NSDictionary bindingDict,它會去找DefaultBindings.qskeys(如果有user自定的,會再去找KeyBindings.qskeys),並讀入「string與action的對應表」。


按下"."後,keyDown: 呼叫 handleBoundKey: ,就在這裡找到這個string有特殊binding。




另外,transmogrifyWithText:這個method,似乎就在做這個「文字模式」。

QSSearchObjectView的keyDown:做了什麼?

先來看原始碼。以下是我刪去除錯訊息和timestamp設定的精簡版程式碼:


// This method deals with all keydowns. Some very interesting things could be done by manipulating this method
- (void)keyDown:(NSEvent *)theEvent {
 
    // ***warning  * have downshift move to indirect object
    if ([[theEvent charactersIgnoringModifiers] isEqualToString:@"/"] && [self handleSlashEvent:theEvent])        return;
    if (([[theEvent characters] isEqualToString:@"~"] || [[theEvent characters] isEqualToString:@"`"]) && [self handleTildeEvent:theEvent])
        return;
    if ([self handleBoundKey:theEvent])
        return;
  
    if ([[theEvent charactersIgnoringModifiers] isEqualToString:@" "]) {        [self insertSpace:nil];
        return;
    }
  
    // ***warning  * have downshift move to indirect object
    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"Shift Actions"]        && [theEvent modifierFlags] &NSShiftKeyMask
        && ([[theEvent characters] length] >= 1)
        && [[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[[theEvent characters] characterAtIndex:0]]        && self == [self directSelector]) {
        [self handleShiftedKeyEvent:theEvent];
        return;
    }
  
    if ([theEvent isARepeat] && !([theEvent modifierFlags] &NSFunctionKeyMask) )
        if ([self handleRepeaterEvent:theEvent]) return;
  
  
    //if (VERBOSE) NSLog(@"interpret");
    [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];    return;}
 可以發現它針對某些狀況做了處理,並把event分派給自定method,諸如:

handleSlashEvent
handleTildeEvent
handleBoundKey
insertSpace
以及我不太了解的兩個:
handleShiftedKeyEvent
handleRepeaterEvent
最後,交給
interpretKeyEvents  (這個method定義在NSResponder中)


補充,關於interpretKeyEvents。
這個函式的說明如下: 
Discussion
This method sends the character input in eventArray to the system input manager for interpretation as text to insert or commands to perform. The input manager responds to the request by sending insertText: and doCommandBySelector: messages back to the invoker of this method. Subclasses shouldn’t override this method.
See the NSInputManager and NSTextInput class and protocol specifications for more information on input management. 
可以發現,其中NSInpitManager用灰體字,也沒有連結;而NSTextInput有。
google NSInpitManager可以找到這份文件:
NSInputManager Class Reference (Not Recommended)
想來處理input有兩種方法,其一是在NSResponder中呼叫interpretKeyEvents;
其二是在NSView打一個handleEvent:給[self inputContext]。
前者使用input manager機制,應該是歷史的產物吧!雖保留但不建議使用;後者使用input context機制,建議使用。

回歸正題。
接下來的問題是,如果不重寫keyDown,則重點在於讓input context打doCommandBySelector:回來。要如何讓input context選擇正確的selector呢?






解析input context與keyDown

首先,它是NSControl的子類別,而非NSTextView的子類別。

不過它實作了NSTextInput protocol。


這樣它還能適用Cocoa Text Editing的規則嗎?

回顧Cocoa Text Editing流程Overview of Text Editing,當中的圖片顯示,
NSTextView會打一個handleEvent:給NSTextInputContext。
Text-input key event processing

那麼,非NSTextView的QSSearchObjectView,應該宣告一個自己的NSTextInputContext property嗎?
經過一番努力,發現每個NSView已經擁有自己的NSTextInputContext

補充一下發現的過程。在查找NSTextInputContext的class reference時,發現它的property "client"的說明
Discussion
The client (owner) of the input context, typically an NSView instance, retains its NSTextInputContext instance. TheNSTextInputContext instance doesn't retain its client. 
當中提到NSView會maintain一個NSTextInputContext。
從這裡我發現了keyDown的正確改寫方法:

- (void)keyDown:(NSEvent *)theEvent {
    // add your code here   
    [[self inputContext] handleEvent:theEvent];  
}
做完你想做的處理,還是要把event交給input context處理。
不過,一如之前所說,不應該去改keyDown。

在黑暗中摸索--撰寫delegate

能學會撰寫delegate,要感謝CocoaHeads Taipei在Podcast的演講影片,
看過yllan的「如何用 Cocoa 為現有的 command-line 程式寫圖形介面」演講,以及
google到的用Xcode和Interface Builder寫delegate這篇文章後,跌跌撞撞也終於讓我搞懂了。

教訓一:寫完實作protocol的class,它會出現在IB的「Library」小視窗中(通常是藍色方塊)。
記得要手動把它拖到「MainMenu.xib」小視窗,才能玩連連看哦!
這個搞了我好久(哭哭)
按住control,滑鼠按住視覺元件並拖到小方塊上、放開滑鼠,選擇"delegate",就完成了。


接下來是實驗結果。
我實作NSTextViewDelegate的兩個method:

- (void)textDidChange:


- (BOOL)textView: doCommandBySelector

結論是,在text view中打任何字(含中英文及空白),會執行textDidChange。
而舉凡enter(return)、tab、shift-tab、Command-C、Ctrl-C等平常使用的「功能鍵」、「組合鍵」都會跑去執行textView: doCommandBySelector。

所以QS提供的特殊指令,例如按下"."進入純文字模式、按下","啟動comma trick、按下"~"切換到home、長按"/"切換到root等,都不能在textView: doCommandBySelector中處理。

2011年11月5日 星期六

找到好東西了!

Text Editing Programming Guide 
Delegate Messages and Notifications


Although you could alter the text view’s behavior by subclassing the text view and overriding insertText:replacementRange: and doCommandBySelector:, a better solution is to handle the event in the text view’s delegate. The delegate can take control over user changes to text by implementing the textView:shouldChangeTextInRange:replacementString: method.
To handle keystrokes that don’t insert text, the delegate can implement the textView:doCommandBySelector: method.



Note:  To modify editing behavior, your first resort should be to notification or delegation, rather than subclassing. It may be tempting to start by subclassing NSTextView and overriding keyDown:, but that’s usually not appropriate, unless you really need to deal with raw key events before input management or key binding. In most cases it’s more appropriate to work with one of the text view delegate methods or with text view notifications.




NSTextInputContext


Search Fields


Quicksilver讀碼日記(一)

修改QS的初衷,是希望這個軟體能支援中文。
As a app launcher,不需要中文,but,  as a desktop search engine,支援中文與否直接決定它的可用性。

感謝Patrick Robertson和Rob McBroom的協助,告訴我應該看QSCollectingSearchObjectView.m和QSInterfaceController.m,
今天研究了下QSCollectingSearchObjectView.m/.h。

這個類別就是Pane1和Pane3,接受user的keyboard input。

它的繼承架構如下:
QSCollectingSearchObjectView
QSSearchObjectView
QSObjectView
NSControl
NSView
NSResponder
NSObject

在QSSearchObjectView.h中宣告如下:

@interface QSSearchObjectView : QSObjectView <NSTextInput
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
    , NSTextDelegate
#endif
{   

...
}



QSSearchObjectView使用了NSTextInput這個protocol,並實作
setMarkedText:selectedRange: 
insertText:
doCommandBySelector:
等幾個method。
其中doCommandBySelector:只是單純的呼叫
[super doCommandBySelector:aSelector]
。另外,關於doCommandBySelector:我發現一件奇怪的事,那就是
QSSearchObjectView有實作textView:doCommandBySelector:,這是NSTextViewDelegate這個protocol的一個method。不過它在宣告明明用到的是NSTextDelegate而非NSTextViewDelegate耶!?
delegate是什麼我也不太清楚,暫時先記著。


關於Cocoa的文字系統,有幾個重要的方法,根據
http://www.cocoabuilder.com/archive/cocoa/156808-keydown-and-japanese-input-methods.html
這篇文章解釋運作原理似乎是這樣的:
keyDown是進入點,基本上不太該改(QS可是大刀闊斧的改了阿XDD)
因為一不小心就會傷害到你的輸入法(這就是我現在的挑戰!)。
key stroke進入keyDown後會被傳送到key binding system,然後以

setMarkedText:selectedRange:replacementRange:
insertText:replacementRange:
doCommandBySelector:


這三種形式回來。它們都是NSTextInputClient Protocol的method。
後者在NSResponder中有實作,也可在NSTextInput或NSTextInputClient這兩個protocol覆寫之。




這裡就是我們可以介入的地方了,可以在subclass重寫,或者是delegate(它到底是什麼@@)。
insertText:replacementRange:可以接收NSString或是NSAttributedString這兩種object。前者是平常用途,後者在使用某些輸入法時會用到。我想中文就是其中一種吧!

然後這篇文章建議,如果要對enter/return做特別處理,應該要去實作
- (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)
aSelector;
這個method。我測了一下,發現QS的這個method雖然有實作,但都沒被call到。
如果要在subclass中做,就把doCommandBySelector:重寫吧。

最後,要知道一個字是不是打完還是打到一半,要用NSTextInput protocol的-hasMarkedText這個method。

2011年11月1日 星期二

IO作業:UART學習資源

IO的第一份作業要trace i-boot-lite,一個bootloader,其中有一題是trace "init_serial()"這個function。
我在serial_xscale.c中找到它,而它使用了許多在start_xscale.h中定義的巨集。
當中有許多看的霧煞煞的詞彙,諸如
FFTHR、FFLCR、FFLSR...等等,故做此記錄。

http://www.lammertbies.nl/comm/info/serial-uart.html
http://babbage.cs.qc.edu/courses/cs343/UART/

另外一份關於C的關鍵字volatile的資料如下:
http://ethanjob.blogspot.com/2007/02/volatile.html