OSDN Git Service

Merge "CMFileManager: Update Polish language" into cm-10.1
[android-x86/packages-apps-CMFileManager.git] / src / com / cyanogenmod / filemanager / activities / EditorActivity.java
1 /*
2  * Copyright (C) 2012 The CyanogenMod Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.cyanogenmod.filemanager.activities;
18
19 import android.app.ActionBar;
20 import android.app.Activity;
21 import android.app.AlertDialog;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.DialogInterface.OnClickListener;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.res.Configuration;
29 import android.graphics.Typeface;
30 import android.graphics.drawable.Drawable;
31 import android.os.AsyncTask;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.preference.PreferenceActivity;
35 import android.text.Editable;
36 import android.text.InputType;
37 import android.text.SpannableStringBuilder;
38 import android.text.TextUtils;
39 import android.text.TextWatcher;
40 import android.util.Log;
41 import android.view.KeyEvent;
42 import android.view.MenuItem;
43 import android.view.View;
44 import android.view.ViewGroup;
45 import android.widget.AdapterView;
46 import android.widget.AdapterView.OnItemClickListener;
47 import android.widget.EditText;
48 import android.widget.ImageView;
49 import android.widget.ListPopupWindow;
50 import android.widget.ProgressBar;
51 import android.widget.TextView;
52 import android.widget.TextView.BufferType;
53 import android.widget.Toast;
54
55 import com.android.internal.util.HexDump;
56 import com.cyanogenmod.filemanager.R;
57 import com.cyanogenmod.filemanager.activities.preferences.EditorPreferenceFragment;
58 import com.cyanogenmod.filemanager.activities.preferences.EditorSHColorSchemePreferenceFragment;
59 import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences;
60 import com.cyanogenmod.filemanager.adapters.HighlightedSimpleMenuListAdapter;
61 import com.cyanogenmod.filemanager.adapters.SimpleMenuListAdapter;
62 import com.cyanogenmod.filemanager.ash.HighlightColors;
63 import com.cyanogenmod.filemanager.ash.ISyntaxHighlightResourcesResolver;
64 import com.cyanogenmod.filemanager.ash.SyntaxHighlightFactory;
65 import com.cyanogenmod.filemanager.ash.SyntaxHighlightProcessor;
66 import com.cyanogenmod.filemanager.commands.AsyncResultListener;
67 import com.cyanogenmod.filemanager.commands.WriteExecutable;
68 import com.cyanogenmod.filemanager.console.ConsoleBuilder;
69 import com.cyanogenmod.filemanager.model.FileSystemObject;
70 import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
71 import com.cyanogenmod.filemanager.preferences.Preferences;
72 import com.cyanogenmod.filemanager.ui.ThemeManager;
73 import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
74 import com.cyanogenmod.filemanager.ui.widgets.ButtonItem;
75 import com.cyanogenmod.filemanager.util.AndroidHelper;
76 import com.cyanogenmod.filemanager.util.CommandHelper;
77 import com.cyanogenmod.filemanager.util.DialogHelper;
78 import com.cyanogenmod.filemanager.util.ExceptionUtil;
79 import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult;
80 import com.cyanogenmod.filemanager.util.FileHelper;
81 import com.cyanogenmod.filemanager.util.ResourcesHelper;
82
83 import java.io.ByteArrayInputStream;
84 import java.io.ByteArrayOutputStream;
85 import java.io.File;
86 import java.io.OutputStream;
87 import java.util.Arrays;
88 import java.util.UUID;
89
90 /**
91  * An internal activity for view and edit files.
92  */
93 public class EditorActivity extends Activity implements TextWatcher {
94
95     private static final String TAG = "EditorActivity"; //$NON-NLS-1$
96
97     private static boolean DEBUG = false;
98
99     private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
100         @Override
101         public void onReceive(Context context, Intent intent) {
102             if (intent != null) {
103                 if (intent.getAction().compareTo(FileManagerSettings.INTENT_THEME_CHANGED) == 0) {
104                     applyTheme();
105                     return;
106                 }
107                 if (intent.getAction().compareTo(FileManagerSettings.INTENT_SETTING_CHANGED) == 0) {
108                     // The settings has changed
109                     String key = intent.getStringExtra(FileManagerSettings.EXTRA_SETTING_CHANGED_KEY);
110                     if (key != null) {
111                         final EditorActivity activity = EditorActivity.this;
112
113                         // No suggestions
114                         if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId()) == 0) {
115                             // Ignore in binary files
116                             if (activity.mBinary) return;
117
118                             // Do we have a different setting?
119                             boolean noSuggestionsSetting =
120                                     Preferences.getSharedPreferences().getBoolean(
121                                         FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId(),
122                                         ((Boolean)FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.
123                                                 getDefaultValue()).booleanValue());
124                             if (noSuggestionsSetting != activity.mNoSuggestions) {
125                                 activity.mHandler.post(new Runnable() {
126                                     @Override
127                                     public void run() {
128                                         toggleNoSuggestions();
129                                     }
130                                 });
131                             }
132
133                         // Word wrap
134                         } else if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId()) == 0) {
135                             // Ignore in binary files
136                             if (activity.mBinary) return;
137
138                             // Do we have a different setting?
139                             boolean wordWrapSetting = Preferences.getSharedPreferences().getBoolean(
140                                     FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId(),
141                                     ((Boolean)FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.
142                                             getDefaultValue()).booleanValue());
143                             if (wordWrapSetting != activity.mWordWrap) {
144                                 activity.mHandler.post(new Runnable() {
145                                     @Override
146                                     public void run() {
147                                         toggleWordWrap();
148                                     }
149                                 });
150                             }
151
152                         // Syntax highlight
153                         // Default theme color scheme
154                         // Color scheme
155                         } else if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId()) == 0) {
156                             // Ignore in binary files
157                             if (activity.mBinary) return;
158
159                             // Do we have a different setting?
160                             boolean syntaxHighlightSetting =
161                                     Preferences.getSharedPreferences().getBoolean(
162                                         FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId(),
163                                         ((Boolean)FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.
164                                                 getDefaultValue()).booleanValue());
165                             if (syntaxHighlightSetting != activity.mSyntaxHighlight) {
166                                 activity.mHandler.post(new Runnable() {
167                                     @Override
168                                     public void run() {
169                                         toggleSyntaxHighlight();
170                                     }
171                                 });
172                             }
173
174                         } else if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId()) == 0 ||
175                                    key.compareTo(FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getId()) == 0 ) {
176                             // Ignore in binary files
177                             if (activity.mBinary) return;
178
179                             // Reload the syntax highlight
180                             activity.mHandler.post(new Runnable() {
181                                 @Override
182                                 public void run() {
183                                     reloadSyntaxHighlight();
184                                 }
185                             });
186                         }
187                     }
188                     return;
189                 }
190             }
191         }
192     };
193
194     /**
195      * Internal interface to notify progress update
196      */
197     private interface OnProgressListener {
198         void onProgress(int progress);
199     }
200
201     /**
202      * An internal listener for read a file
203      */
204     private class AsyncReader implements AsyncResultListener {
205
206         final Object mSync = new Object();
207         ByteArrayOutputStream mByteBuffer = null;
208         SpannableStringBuilder mBuffer = null;
209         Exception mCause;
210         long mSize;
211         FileSystemObject mReadFso;
212         OnProgressListener mListener;
213
214         /**
215          * Constructor of <code>AsyncReader</code>. For enclosing access.
216          */
217         public AsyncReader() {
218             super();
219         }
220
221         /**
222          * {@inheritDoc}
223          */
224         @Override
225         public void onAsyncStart() {
226             this.mByteBuffer = new ByteArrayOutputStream((int)this.mReadFso.getSize());
227             this.mSize = 0;
228         }
229
230         /**
231          * {@inheritDoc}
232          */
233         @Override
234         public void onAsyncEnd(boolean cancelled) {/**NON BLOCK**/}
235
236         /**
237          * {@inheritDoc}
238          */
239         @Override
240         public void onAsyncExitCode(int exitCode) {
241             synchronized (this.mSync) {
242                 this.mSync.notify();
243             }
244         }
245
246         /**
247          * {@inheritDoc}
248          */
249         @Override
250         public void onPartialResult(Object result) {
251             try {
252                 if (result == null) return;
253                 byte[] partial = (byte[])result;
254
255                 // Check if the file is a binary file. In this case the editor
256                 // is read-only
257                 if (!EditorActivity.this.mReadOnly) {
258                     for (int i = 0; i < partial.length-1; i++) {
259                         if (!isPrintableCharacter((char)partial[i])) {
260                             EditorActivity.this.mBinary = true;
261                             EditorActivity.this.mReadOnly = true;
262                             break;
263                         }
264                     }
265                 }
266
267                 this.mByteBuffer.write(partial, 0, partial.length);
268                 this.mSize += partial.length;
269                 if (this.mListener != null && this.mReadFso != null) {
270                     int progress = 0;
271                     if (this.mReadFso.getSize() != 0) {
272                         progress = (int)((this.mSize*100) / this.mReadFso.getSize());
273                     }
274                     this.mListener.onProgress(progress);
275                 }
276             } catch (Exception e) {
277                 this.mCause = e;
278             }
279         }
280
281         /**
282          * {@inheritDoc}
283          */
284         @Override
285         public void onException(Exception cause) {
286             this.mCause = cause;
287         }
288     }
289
290     /**
291      * An internal listener for write a file
292      */
293     private class AsyncWriter implements AsyncResultListener {
294
295         Exception mCause;
296
297         /**
298          * Constructor of <code>AsyncWriter</code>. For enclosing access.
299          */
300         public AsyncWriter() {
301             super();
302         }
303
304         /**
305          * {@inheritDoc}
306          */
307         @Override
308         public void onAsyncStart() {/**NON BLOCK**/}
309
310         /**
311          * {@inheritDoc}
312          */
313         @Override
314         public void onAsyncEnd(boolean cancelled) {/**NON BLOCK**/}
315
316         /**
317          * {@inheritDoc}
318          */
319         @Override
320         public void onAsyncExitCode(int exitCode) {/**NON BLOCK**/}
321
322         /**
323          * {@inheritDoc}
324          */
325         @Override
326         public void onPartialResult(Object result) {/**NON BLOCK**/}
327
328         /**
329          * {@inheritDoc}
330          */
331         @Override
332         public void onException(Exception cause) {
333             this.mCause = cause;
334         }
335     }
336
337     /**
338      * An internal class to resolve resources for the syntax highlight library.
339      * @hide
340      */
341     class ResourcesResolver implements ISyntaxHighlightResourcesResolver {
342         @Override
343         public CharSequence getString(String id, String resid) {
344             return EditorActivity.this.getString(
345                         ResourcesHelper.getIdentifier(
346                             EditorActivity.this.getResources(), "string", resid)); //$NON-NLS-1$
347         }
348
349         @Override
350         public int getInteger(String id, String resid, int def) {
351             return EditorActivity.this.getResources().
352                     getInteger(
353                         ResourcesHelper.getIdentifier(
354                             EditorActivity.this.getResources(), "integer", resid)); //$NON-NLS-1$
355         }
356
357         @Override
358         public int getColor(String id, String resid, int def) {
359             final Context ctx = EditorActivity.this;
360             try {
361                 // Is default theme color scheme enabled?
362                 if (isDefaultThemeColorScheme()) {
363                     return ThemeManager.getCurrentTheme(ctx).getColor(ctx, resid);
364                 }
365
366                 // Use the user-defined settings
367                 int[] colors = getUserColorScheme();
368                 HighlightColors[] schemeColors = HighlightColors.values();
369                 int cc = schemeColors.length;
370                 int cc2 = colors.length;
371                 for (int i = 0; i < cc; i++) {
372                     if (schemeColors[i].getId().compareTo(id) == 0) {
373                         if (cc2 >= i) {
374                             // User-defined
375                             return colors[i];
376                         }
377
378                         // Theme default
379                         return ThemeManager.getCurrentTheme(ctx).getColor(ctx, resid);
380                     }
381
382                 }
383
384             } catch (Exception ex) {
385                 // Resource not found
386             }
387             return def;
388         }
389
390         /**
391          * Method that returns if we should return the default theme color scheme or not
392          *
393          * @return boolean Whether return the default theme color scheme or not
394          */
395         private boolean isDefaultThemeColorScheme() {
396             Boolean defaultValue =
397                     (Boolean)FileManagerSettings.
398                                 SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getDefaultValue();
399             return Preferences.getSharedPreferences().getBoolean(
400                         FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId(),
401                         defaultValue.booleanValue());
402         }
403
404         /**
405          * Method that returns the user-defined color scheme
406          *
407          * @return int[] The user-defined color scheme
408          */
409         private int[] getUserColorScheme() {
410             String defaultValue =
411                     (String)FileManagerSettings.
412                                 SETTINGS_EDITOR_SH_COLOR_SCHEME.getDefaultValue();
413             String value = Preferences.getSharedPreferences().getString(
414                                 FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getId(),
415                                 defaultValue);
416             return EditorSHColorSchemePreferenceFragment.toColorShemeArray(value);
417         }
418     }
419
420     /**
421      * @hide
422      */
423     FileSystemObject mFso;
424
425     private int mBufferSize;
426     private int mMaxFileSize;
427
428     /**
429      * @hide
430      */
431     boolean mDirty;
432     /**
433      * @hide
434      */
435     boolean mReadOnly;
436     /**
437      * @hide
438      */
439     boolean mBinary;
440
441     /**
442      * @hide
443      */
444     TextView mTitle;
445     /**
446      * @hide
447      */
448     EditText mEditor;
449     /**
450      * @hide
451      */
452     View mProgress;
453     /**
454      * @hide
455      */
456     ProgressBar mProgressBar;
457     /**
458      * @hide
459      */
460     TextView mProgressBarMsg;
461     /**
462      * @hide
463      */
464     ButtonItem mSave;
465
466     // No suggestions status
467     /**
468      * @hide
469      */
470     boolean mNoSuggestions;
471
472     // Word wrap status
473     private ViewGroup mWordWrapView;
474     private ViewGroup mNoWordWrapView;
475     /**
476      * @hide
477      */
478     boolean mWordWrap;
479
480     // Syntax highlight status
481     /**
482      * @hide
483      */
484     boolean mSyntaxHighlight;
485     /**
486      * @hide
487      */
488     SyntaxHighlightProcessor mSyntaxHighlightProcessor;
489     private int mEditStart;
490     private int mEditEnd;
491
492     private View mOptionsAnchorView;
493
494     private final Object mExecSync = new Object();
495
496     /**
497      * @hide
498      */
499     Handler mHandler;
500
501     private static final char[] VALID_NON_PRINTABLE_CHARS = {' ', '\t', '\r', '\n'};
502
503     /**
504      * @hide
505      */
506     String mHexLineSeparator;
507
508     /**
509      * Intent extra parameter for the path of the file to open.
510      */
511     public static final String EXTRA_OPEN_FILE = "extra_open_file";  //$NON-NLS-1$
512
513     /**
514      * {@inheritDoc}
515      */
516     @Override
517     protected void onCreate(Bundle state) {
518         if (DEBUG) {
519             Log.d(TAG, "EditorActivity.onCreate"); //$NON-NLS-1$
520         }
521
522         this.mHandler = new Handler();
523
524         // Register the broadcast receiver
525         IntentFilter filter = new IntentFilter();
526         filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED);
527         filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED);
528         registerReceiver(this.mNotificationReceiver, filter);
529
530         // Generate a random separator
531         this.mHexLineSeparator = UUID.randomUUID().toString();
532
533         //Set the main layout of the activity
534         setContentView(R.layout.editor);
535
536         // Get the limit vars
537         this.mBufferSize =
538                 getApplicationContext().getResources().getInteger(R.integer.buffer_size);
539         this.mMaxFileSize =
540                 getApplicationContext().getResources().getInteger(R.integer.editor_max_file_size);
541
542         //Initialize
543         initTitleActionBar();
544         initLayout();
545
546         // Apply the theme
547         applyTheme();
548
549         // Initialize the console
550         initializeConsole();
551
552         // Read the file
553         readFile();
554
555         //Save state
556         super.onCreate(state);
557     }
558
559     /**
560      * {@inheritDoc}
561      */
562     @Override
563     protected void onDestroy() {
564         if (DEBUG) {
565             Log.d(TAG, "EditorActivity.onDestroy"); //$NON-NLS-1$
566         }
567
568         // Unregister the receiver
569         try {
570             unregisterReceiver(this.mNotificationReceiver);
571         } catch (Throwable ex) {
572             /**NON BLOCK**/
573         }
574
575         //All destroy. Continue
576         super.onDestroy();
577     }
578
579     /**
580      * {@inheritDoc}
581      */
582     @Override
583     public void onConfigurationChanged(Configuration newConfig) {
584         super.onConfigurationChanged(newConfig);
585     }
586
587     /**
588      * Method that initializes the titlebar of the activity.
589      */
590     private void initTitleActionBar() {
591         //Configure the action bar options
592         getActionBar().setBackgroundDrawable(
593                 getResources().getDrawable(R.drawable.bg_holo_titlebar));
594         getActionBar().setDisplayOptions(
595                 ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
596         getActionBar().setDisplayHomeAsUpEnabled(true);
597         View customTitle = getLayoutInflater().inflate(R.layout.simple_customtitle, null, false);
598         this.mTitle = (TextView)customTitle.findViewById(R.id.customtitle_title);
599         this.mTitle.setText(R.string.editor);
600         this.mTitle.setContentDescription(getString(R.string.editor));
601         this.mSave = (ButtonItem)customTitle.findViewById(R.id.ab_button1);
602         this.mSave.setImageResource(R.drawable.ic_holo_light_save);
603         this.mSave.setContentDescription(getString(R.string.actionbar_button_save_cd));
604         this.mSave.setVisibility(View.GONE);
605
606         ButtonItem configuration = (ButtonItem)customTitle.findViewById(R.id.ab_button2);
607         configuration.setImageResource(R.drawable.ic_holo_light_overflow);
608         configuration.setContentDescription(getString(R.string.actionbar_button_overflow_cd));
609
610         View status = findViewById(R.id.editor_status);
611         boolean showOptionsMenu = AndroidHelper.showOptionsMenu(getApplicationContext());
612         configuration.setVisibility(showOptionsMenu ? View.VISIBLE : View.GONE);
613         this.mOptionsAnchorView = showOptionsMenu ? configuration : status;
614
615         getActionBar().setCustomView(customTitle);
616     }
617
618     /**
619      * Method that initializes the layout and components of the activity.
620      */
621     private void initLayout() {
622         this.mEditor = (EditText)findViewById(R.id.editor);
623         this.mEditor.setText(null);
624         this.mEditor.addTextChangedListener(this);
625         this.mEditor.setEnabled(false);
626         this.mWordWrapView = (ViewGroup)findViewById(R.id.editor_word_wrap_view);
627         this.mNoWordWrapView = (ViewGroup)findViewById(R.id.editor_no_word_wrap_view);
628         this.mWordWrapView.setVisibility(View.VISIBLE);
629         this.mNoWordWrapView.setVisibility(View.GONE);
630
631         this.mNoSuggestions = false;
632         this.mWordWrap = true;
633         this.mSyntaxHighlight = true;
634
635         // Load the no suggestions setting
636         boolean noSuggestionsSetting = Preferences.getSharedPreferences().getBoolean(
637                 FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId(),
638                 ((Boolean)FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.
639                         getDefaultValue()).booleanValue());
640         if (noSuggestionsSetting != this.mNoSuggestions) {
641             toggleNoSuggestions();
642         }
643
644         // Load the word wrap setting
645         boolean wordWrapSetting = Preferences.getSharedPreferences().getBoolean(
646                 FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId(),
647                 ((Boolean)FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.
648                         getDefaultValue()).booleanValue());
649         if (wordWrapSetting != this.mWordWrap) {
650             toggleWordWrap();
651         }
652
653         // Load the syntax highlight setting
654         boolean syntaxHighlighSetting = Preferences.getSharedPreferences().getBoolean(
655                 FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId(),
656                 ((Boolean)FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.
657                         getDefaultValue()).booleanValue());
658         if (syntaxHighlighSetting != this.mSyntaxHighlight) {
659             toggleSyntaxHighlight();
660         }
661
662         this.mProgress = findViewById(R.id.editor_progress);
663         this.mProgressBar = (ProgressBar)findViewById(R.id.editor_progress_bar);
664         this.mProgressBarMsg = (TextView)findViewById(R.id.editor_progress_msg);
665     }
666
667     /**
668      * Method that toggle the no suggestions property of the editor
669      * @hide
670      */
671     /**package**/ void toggleNoSuggestions() {
672         synchronized (this.mExecSync) {
673             int type = InputType.TYPE_CLASS_TEXT |
674                        InputType.TYPE_TEXT_FLAG_MULTI_LINE |
675                        InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE;
676             if (!this.mNoSuggestions) {
677                 type |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
678             }
679             this.mEditor.setInputType(type);
680             this.mNoSuggestions = !this.mNoSuggestions;
681         }
682     }
683
684     /**
685      * Method that toggle the word wrap property of the editor
686      * @hide
687      */
688     /**package**/ void toggleWordWrap() {
689         synchronized (this.mExecSync) {
690             ViewGroup vSrc = this.mWordWrap ? this.mWordWrapView : this.mNoWordWrapView;
691             ViewGroup vDst = this.mWordWrap ? this.mNoWordWrapView : this.mWordWrapView;
692             ViewGroup vSrcParent = this.mWordWrap
693                                                 ? this.mWordWrapView
694                                                 : (ViewGroup)this.mNoWordWrapView.getChildAt(0);
695             ViewGroup vDstParent = this.mWordWrap
696                                                 ? (ViewGroup)this.mNoWordWrapView.getChildAt(0)
697                                                 : this.mWordWrapView;
698             vSrc.setVisibility(View.GONE);
699             vSrcParent.removeView(this.mEditor);
700             vDstParent.addView(this.mEditor);
701             vDst.setVisibility(View.VISIBLE);
702             vDst.scrollTo(0, 0);
703             this.mWordWrap = !this.mWordWrap;
704         }
705     }
706
707     /**
708      * Method that toggles the syntax highlight property of the editor
709      * @hide
710      */
711     /**package**/ void toggleSyntaxHighlight() {
712         synchronized (this.mExecSync) {
713             if (this.mSyntaxHighlightProcessor != null) {
714                 try {
715                     if (this.mSyntaxHighlight) {
716                         this.mSyntaxHighlightProcessor.clear(this.mEditor.getText());
717                     } else {
718                         this.mSyntaxHighlightProcessor.process(this.mEditor.getText());
719                     }
720                 } catch (Exception ex) {
721                     // An error in a syntax library, should not break down app.
722                     Log.e(TAG, "Syntax highlight failed.", ex); //$NON-NLS-1$
723                 }
724             }
725
726             this.mSyntaxHighlight = !this.mSyntaxHighlight;
727         }
728     }
729
730     /**
731      * Method that reloads the syntax highlight of the current file
732      * @hide
733      */
734     /**package**/ void reloadSyntaxHighlight() {
735         synchronized (this.mExecSync) {
736             if (this.mSyntaxHighlightProcessor != null) {
737                 try {
738                     this.mSyntaxHighlightProcessor.initialize();
739                     this.mSyntaxHighlightProcessor.process(this.mEditor.getText());
740                 } catch (Exception ex) {
741                     // An error in a syntax library, should not break down app.
742                     Log.e(TAG, "Syntax highlight failed.", ex); //$NON-NLS-1$
743                 }
744             }
745         }
746     }
747
748     /**
749      * {@inheritDoc}
750      */
751     @Override
752     public boolean onKeyUp(int keyCode, KeyEvent event) {
753         switch (keyCode) {
754             case KeyEvent.KEYCODE_MENU:
755                 showOverflowPopUp(this.mOptionsAnchorView);
756                 return true;
757             case KeyEvent.KEYCODE_BACK:
758                 checkDirtyState();
759                 return true;
760             default:
761                 return super.onKeyUp(keyCode, event);
762         }
763     }
764
765     /**
766      * {@inheritDoc}
767      */
768     @Override
769     public boolean onOptionsItemSelected(MenuItem item) {
770        switch (item.getItemId()) {
771           case android.R.id.home:
772               if ((getActionBar().getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP)
773                       == ActionBar.DISPLAY_HOME_AS_UP) {
774                   checkDirtyState();
775               }
776               return true;
777           default:
778              return super.onOptionsItemSelected(item);
779        }
780     }
781
782     /**
783      * Method that shows a popup with the activity main menu.
784      *
785      * @param anchor The anchor of the popup
786      */
787     private void showOverflowPopUp(View anchor) {
788         SimpleMenuListAdapter adapter =
789                 new HighlightedSimpleMenuListAdapter(this, R.menu.editor);
790         MenuItem noSuggestions = adapter.getMenu().findItem(R.id.mnu_no_suggestions);
791         if (noSuggestions != null) {
792             if (this.mBinary) {
793                 adapter.getMenu().removeItem(R.id.mnu_no_suggestions);
794             } else {
795                 noSuggestions.setChecked(this.mNoSuggestions);
796             }
797         }
798         MenuItem wordWrap = adapter.getMenu().findItem(R.id.mnu_word_wrap);
799         if (wordWrap != null) {
800             if (this.mBinary) {
801                 adapter.getMenu().removeItem(R.id.mnu_word_wrap);
802             } else {
803                 wordWrap.setChecked(this.mWordWrap);
804             }
805         }
806         MenuItem syntaxHighlight = adapter.getMenu().findItem(R.id.mnu_syntax_highlight);
807         if (syntaxHighlight != null) {
808             if (this.mBinary) {
809                 adapter.getMenu().removeItem(R.id.mnu_syntax_highlight);
810             } else {
811                 syntaxHighlight.setChecked(this.mSyntaxHighlight);
812             }
813         }
814
815         final ListPopupWindow popup =
816                 DialogHelper.createListPopupWindow(this, adapter, anchor);
817         popup.setOnItemClickListener(new OnItemClickListener() {
818             @Override
819             public void onItemClick(
820                     final AdapterView<?> parent, final View v,
821                     final int position, final long id) {
822                 final int itemId = (int)id;
823                 switch (itemId) {
824                     case R.id.mnu_no_suggestions:
825                         toggleNoSuggestions();
826                         break;
827                     case R.id.mnu_word_wrap:
828                         toggleWordWrap();
829                         break;
830                     case R.id.mnu_syntax_highlight:
831                         toggleSyntaxHighlight();
832                         break;
833                     case R.id.mnu_settings:
834                         //Settings
835                         Intent settings = new Intent(EditorActivity.this, SettingsPreferences.class);
836                         settings.putExtra(
837                                 PreferenceActivity.EXTRA_SHOW_FRAGMENT,
838                                 EditorPreferenceFragment.class.getName());
839                         startActivity(settings);
840                         break;
841                 }
842                 popup.dismiss();
843             }
844         });
845         popup.show();
846     }
847
848     /**
849      * Method invoked when an action item is clicked.
850      *
851      * @param view The button pushed
852      */
853     public void onActionBarItemClick(View view) {
854         switch (view.getId()) {
855             case R.id.ab_button1:
856                 // Save the file
857                 checkAndWrite();
858                 break;
859
860             case R.id.ab_button2:
861                 // Show overflow menu
862                 showOverflowPopUp(this.mOptionsAnchorView);
863                 break;
864
865             default:
866                 break;
867         }
868     }
869
870     /**
871      * Method that initializes a console
872      */
873     private boolean initializeConsole() {
874         try {
875             ConsoleBuilder.getConsole(this);
876             // There is a console allocated. Use it.
877             return true;
878         } catch (Throwable _throw) {
879             // Capture the exception
880             ExceptionUtil.translateException(this, _throw, false, true);
881         }
882         return false;
883     }
884
885     /**
886      * Method that reads the requested file
887      */
888     private void readFile() {
889         // For now editor is not dirty and editable.
890         setDirty(false);
891         this.mBinary = false;
892
893         // Check for a valid action
894         String action = getIntent().getAction();
895         if (action == null ||
896                 (action.compareTo(Intent.ACTION_VIEW) != 0) &&
897                 (action.compareTo(Intent.ACTION_EDIT) != 0)) {
898             DialogHelper.showToast(
899                     this, R.string.editor_invalid_file_msg, Toast.LENGTH_SHORT);
900             return;
901         }
902         // This var should be set depending on ACTION_VIEW or ACTION_EDIT action, but for
903         // better compatibility, IntentsActionPolicy use always ACTION_VIEW, so we have
904         // to ignore this check here
905         this.mReadOnly = false;
906
907         // Read the intent and check that is has a valid request
908         String path = getIntent().getData().getPath();
909         if (path == null || path.length() == 0) {
910             DialogHelper.showToast(
911                     this, R.string.editor_invalid_file_msg, Toast.LENGTH_SHORT);
912             return;
913         }
914
915         // Set the title of the dialog
916         File f = new File(path);
917         this.mTitle.setText(f.getName());
918
919         // Check that the file exists (the real file, not the symlink)
920         try {
921             this.mFso = CommandHelper.getFileInfo(this, path, true, null);
922             if (this.mFso == null) {
923                 DialogHelper.showToast(
924                         this, R.string.editor_file_not_found_msg, Toast.LENGTH_SHORT);
925                 return;
926             }
927         } catch (Exception e) {
928             Log.e(TAG, "Failed to get file reference", e); //$NON-NLS-1$
929             DialogHelper.showToast(
930                     this, R.string.editor_file_not_found_msg, Toast.LENGTH_SHORT);
931             return;
932         }
933
934         // Check that we can handle the length of the file (by device)
935         if (this.mMaxFileSize < this.mFso.getSize()) {
936             DialogHelper.showToast(
937                     this, R.string.editor_file_exceed_size_msg, Toast.LENGTH_SHORT);
938             return;
939         }
940
941         // Get the syntax highlight processor
942         SyntaxHighlightFactory shpFactory =
943                 SyntaxHighlightFactory.getDefaultFactory(new ResourcesResolver());
944         this.mSyntaxHighlightProcessor = shpFactory.getSyntaxHighlightProcessor(f);
945         if (this.mSyntaxHighlightProcessor != null) {
946             this.mSyntaxHighlightProcessor.initialize();
947         }
948
949         // Check that we have read access
950         try {
951             FileHelper.ensureReadAccess(
952                     ConsoleBuilder.getConsole(this),
953                     this.mFso,
954                     null);
955
956             // Read the file in background
957             asyncRead();
958
959         } catch (Exception ex) {
960             ExceptionUtil.translateException(
961                     this, ex, false, true, new OnRelaunchCommandResult() {
962                 @Override
963                 public void onSuccess() {
964                     // Read the file in background
965                     asyncRead();
966                 }
967
968                 @Override
969                 public void onFailed(Throwable cause) {
970                     finish();
971                 }
972
973                 @Override
974                 public void onCancelled() {
975                     finish();
976                 }
977             });
978         }
979     }
980
981     /**
982      * Method that does the read of the file in background
983      * @hide
984      */
985     void asyncRead() {
986         // Do the load of the file
987         AsyncTask<FileSystemObject, Integer, Boolean> mReadTask =
988                             new AsyncTask<FileSystemObject, Integer, Boolean>() {
989
990             private Exception mCause;
991             private AsyncReader mReader;
992             private boolean changeToBinaryMode;
993             private boolean changeToDisplaying;
994
995             @Override
996             protected void onPreExecute() {
997                 // Show the progress
998                 this.changeToBinaryMode = false;
999                 this.changeToDisplaying = false;
1000                 doProgress(true, 0);
1001             }
1002
1003             @Override
1004             protected Boolean doInBackground(FileSystemObject... params) {
1005                 final EditorActivity activity = EditorActivity.this;
1006
1007                 // Only one argument (the file to open)
1008                 FileSystemObject fso = params[0];
1009                 this.mCause = null;
1010
1011                 // Read the file in an async listener
1012                 try {
1013                     while (true) {
1014                         // Configure the reader
1015                         this.mReader = new AsyncReader();
1016                         this.mReader.mReadFso = fso;
1017                         this.mReader.mListener = new OnProgressListener() {
1018                             @Override
1019                             @SuppressWarnings("synthetic-access")
1020                             public void onProgress(int progress) {
1021                                 publishProgress(Integer.valueOf(progress));
1022                             }
1023                         };
1024
1025                         // Execute the command (read the file)
1026                         CommandHelper.read(activity, fso.getFullPath(), this.mReader, null);
1027
1028                         // Wait for
1029                         synchronized (this.mReader.mSync) {
1030                             this.mReader.mSync.wait();
1031                         }
1032
1033                         // 100%
1034                         publishProgress(new Integer(100));
1035
1036                         // Check if the read was successfully
1037                         if (this.mReader.mCause != null) {
1038                             this.mCause = this.mReader.mCause;
1039                             return Boolean.FALSE;
1040                         }
1041                         break;
1042                     }
1043
1044                     // Now we have the byte array with all the data. is a binary file?
1045                     // Then dump them byte array to hex dump string (only if users settings
1046                     // to dump file)
1047                     boolean hexDump =
1048                             Preferences.getSharedPreferences().getBoolean(
1049                                 FileManagerSettings.SETTINGS_EDITOR_HEXDUMP.getId(),
1050                                 ((Boolean)FileManagerSettings.SETTINGS_EDITOR_HEXDUMP.
1051                                         getDefaultValue()).booleanValue());
1052                     if (activity.mBinary && hexDump) {
1053                         // we do not use the Hexdump helper class, because we need to show the
1054                         // progress of the dump process
1055                         this.mReader.mBuffer =
1056                                 new SpannableStringBuilder(
1057                                         toHexPrintableString(
1058                                                 toHexDump(
1059                                                         this.mReader.mByteBuffer.toByteArray())));
1060                     } else {
1061                         this.mReader.mBuffer =
1062                                 new SpannableStringBuilder(
1063                                         new String(this.mReader.mByteBuffer.toByteArray()));
1064                     }
1065                     this.mReader.mByteBuffer = null;
1066
1067                     // 100%
1068                     this.changeToDisplaying = true;
1069                     publishProgress(new Integer(0));
1070
1071                 } catch (Exception e) {
1072                     this.mCause = e;
1073                     return Boolean.FALSE;
1074                 }
1075
1076                 return Boolean.TRUE;
1077             }
1078
1079             @Override
1080             protected void onProgressUpdate(Integer... values) {
1081                 // Do progress
1082                 doProgress(true, values[0].intValue());
1083             }
1084
1085             @Override
1086             protected void onPostExecute(Boolean result) {
1087                 final EditorActivity activity = EditorActivity.this;
1088
1089                 // Is error?
1090                 if (!result.booleanValue()) {
1091                     if (this.mCause != null) {
1092                         ExceptionUtil.translateException(activity, this.mCause);
1093                         activity.mEditor.setEnabled(false);
1094                     }
1095                 } else {
1096                     // Now we have the buffer, set the text of the editor
1097                     if (activity.mBinary) {
1098                         activity.mEditor.setText(
1099                                 this.mReader.mBuffer, BufferType.NORMAL);
1100                     } else {
1101                         activity.mEditor.setText(
1102                                 this.mReader.mBuffer, BufferType.EDITABLE);
1103
1104                         // Highlight editor text syntax
1105                         if (activity.mSyntaxHighlight &&
1106                             activity.mSyntaxHighlightProcessor != null) {
1107                             try {
1108                                 activity.mSyntaxHighlightProcessor.process(
1109                                         activity.mEditor.getText());
1110                             } catch (Exception ex) {
1111                                 // An error in a syntax library, should not break down app.
1112                                 Log.e(TAG, "Syntax highlight failed.", ex); //$NON-NLS-1$
1113                             }
1114                         }
1115                     }
1116                     this.mReader.mBuffer = null; //Cleanup
1117                     setDirty(false);
1118                     activity.mEditor.setEnabled(!activity.mReadOnly);
1119
1120                     // Notify read-only mode
1121                     if (activity.mReadOnly) {
1122                         DialogHelper.showToast(
1123                                 activity,
1124                                 R.string.editor_read_only_mode,
1125                                 Toast.LENGTH_SHORT);
1126                     }
1127                 }
1128
1129                 doProgress(false, 0);
1130             }
1131
1132             @Override
1133             protected void onCancelled() {
1134                 // Hide the progress
1135                 doProgress(false, 0);
1136             }
1137
1138             /**
1139              * Method that update the progress status
1140              *
1141              * @param visible If the progress bar need to be hidden
1142              * @param progress The progress
1143              */
1144             private void doProgress(boolean visible, int progress) {
1145                 final EditorActivity activity = EditorActivity.this;
1146
1147                 // Show the progress bar
1148                 activity.mProgressBar.setProgress(progress);
1149                 activity.mProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
1150
1151                 if (this.changeToBinaryMode) {
1152                     // Hexdump always in nowrap mode
1153                     if (activity.mWordWrap) {
1154                         activity.toggleWordWrap();
1155                     }
1156                     // Hexdump always has no syntax highlight
1157                     if (activity.mSyntaxHighlight) {
1158                         activity.toggleSyntaxHighlight();
1159                     }
1160
1161                     // Show hex dumping text
1162                     activity.mProgressBarMsg.setText(R.string.dumping_message);
1163                     activity.mEditor.setTextAppearance(activity, R.style.hexeditor_text_appearance);
1164                     activity.mEditor.setTypeface(Typeface.MONOSPACE);
1165                     this.changeToBinaryMode = false;
1166                 }
1167                 else if (this.changeToDisplaying) {
1168                     activity.mProgressBarMsg.setText(R.string.displaying_message);
1169                     this.changeToDisplaying = false;
1170                 }
1171             }
1172
1173             /**
1174              * Create a hex dump of the data while show progress to user
1175              *
1176              * @param data The data to hex dump
1177              * @return StringBuilder The hex dump buffer
1178              */
1179             private String toHexDump(byte[] data) {
1180                 //Change to binary mode
1181                 this.changeToBinaryMode = true;
1182
1183                 // Start progress
1184                 publishProgress(Integer.valueOf(0));
1185
1186                 // Calculate max dir size
1187                 int length = data.length;
1188
1189                 final int DISPLAY_SIZE = 16;  // Bytes per line
1190                 ByteArrayInputStream bais = new ByteArrayInputStream(data);
1191                 byte[] line = new byte[DISPLAY_SIZE];
1192                 int read = 0;
1193                 int offset = 0;
1194                 StringBuilder sb = new StringBuilder();
1195                 while ((read = bais.read(line, 0, DISPLAY_SIZE)) != -1) {
1196                     //offset   dump(16)   data\n
1197                     String linedata = new String(line, 0, read);
1198                     sb.append(HexDump.toHexString(offset));
1199                     sb.append("   "); //$NON-NLS-1$
1200                     String hexDump = HexDump.toHexString(line, 0, read);
1201                     if (hexDump.length() != (DISPLAY_SIZE * 2)) {
1202                         char[] array = new char[(DISPLAY_SIZE * 2) - hexDump.length()];
1203                         Arrays.fill(array, ' ');
1204                         hexDump += new String(array);
1205                     }
1206                     sb.append(hexDump);
1207                     sb.append("   "); //$NON-NLS-1$
1208                     sb.append(linedata);
1209                     sb.append(EditorActivity.this.mHexLineSeparator);
1210                     offset += DISPLAY_SIZE;
1211                     if (offset % 5 == 0) {
1212                         publishProgress(Integer.valueOf((offset * 100) / length));
1213                     }
1214                 }
1215
1216                 // End of the dump process
1217                 publishProgress(Integer.valueOf(100));
1218
1219                 return sb.toString();
1220             }
1221
1222             /**
1223              * Method that converts to a visual printable hex string
1224              *
1225              * @param string The string to check
1226              */
1227             private String toHexPrintableString(String string) {
1228                 // Remove characters without visual representation
1229                 final String REPLACED_SYMBOL = "."; //$NON-NLS-1$
1230                 final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
1231                 String printable = string.replaceAll("\\p{Cntrl}", REPLACED_SYMBOL); //$NON-NLS-1$
1232                 printable = printable.replaceAll("[^\\p{Print}]", REPLACED_SYMBOL); //$NON-NLS-1$
1233                 printable = printable.replaceAll("\\p{C}", REPLACED_SYMBOL); //$NON-NLS-1$
1234                 printable = printable.replaceAll(EditorActivity.this.mHexLineSeparator, NEWLINE);
1235                 return printable;
1236             }
1237         };
1238         mReadTask.execute(this.mFso);
1239     }
1240
1241     private void checkAndWrite() {
1242         // Check that we have write access
1243         try {
1244             FileHelper.ensureWriteAccess(
1245                     ConsoleBuilder.getConsole(this),
1246                     this.mFso,
1247                     null);
1248
1249             // Write the file
1250             syncWrite();
1251
1252         } catch (Exception ex) {
1253             ExceptionUtil.translateException(
1254                     this, ex, false, true, new OnRelaunchCommandResult() {
1255                 @Override
1256                 public void onSuccess() {
1257                     // Write the file
1258                     syncWrite();
1259                 }
1260
1261                 @Override
1262                 public void onFailed(Throwable cause) {/**NON BLOCK**/}
1263
1264                 @Override
1265                 public void onCancelled() {/**NON BLOCK**/}
1266             });
1267         }
1268     }
1269
1270     /**
1271      * Method that write the file.
1272      * @hide
1273      */
1274     void syncWrite() {
1275         try {
1276             // Configure the writer
1277             AsyncWriter writer = new AsyncWriter();
1278
1279             // Create the writable command
1280             WriteExecutable cmd =
1281                     CommandHelper.write(this, this.mFso.getFullPath(), writer, null);
1282
1283             // Obtain access to the buffer (IMP! don't close the buffer here, it's manage
1284             // by the command)
1285             OutputStream os = cmd.createOutputStream();
1286             try {
1287                 // Retrieve the text from the editor
1288                 String text = this.mEditor.getText().toString();
1289                 ByteArrayInputStream bais = new ByteArrayInputStream(text.getBytes());
1290                 text = null;
1291                 try {
1292                     // Buffered write
1293                     byte[] data = new byte[this.mBufferSize];
1294                     int read = 0;
1295                     while ((read = bais.read(data, 0, this.mBufferSize)) != -1) {
1296                         os.write(data, 0, read);
1297                     }
1298                 } finally {
1299                     try {
1300                         bais.close();
1301                     } catch (Exception e) {/**NON BLOCK**/}
1302                 }
1303
1304             } finally {
1305                 // Ok. Data is written or ensure buffer close
1306                 cmd.end();
1307             }
1308
1309             // Sleep a bit
1310             Thread.sleep(150L);
1311
1312             // Is error?
1313             if (writer.mCause != null) {
1314                 // Something was wrong. The file probably is corrupted
1315                 DialogHelper.showToast(
1316                         this, R.string.msgs_operation_failure, Toast.LENGTH_SHORT);
1317             } else {
1318                 // Success. The file was saved
1319                 DialogHelper.showToast(
1320                         this, R.string.editor_successfully_saved, Toast.LENGTH_SHORT);
1321                 setDirty(false);
1322
1323                 // Send a message that allow other activities to update his data
1324                 Intent intent = new Intent(FileManagerSettings.INTENT_FILE_CHANGED);
1325                 intent.putExtra(
1326                         FileManagerSettings.EXTRA_FILE_CHANGED_KEY, this.mFso.getFullPath());
1327                 sendBroadcast(intent);
1328             }
1329
1330         } catch (Exception e) {
1331             // Something was wrong, but the file was NOT written
1332             DialogHelper.showToast(
1333                     this, R.string.msgs_operation_failure, Toast.LENGTH_SHORT);
1334             return;
1335         }
1336     }
1337
1338     /**
1339      * {@inheritDoc}
1340      */
1341     @Override
1342     public void beforeTextChanged(
1343             CharSequence s, int start, int count, int after) {/**NON BLOCK**/}
1344
1345     /**
1346      * {@inheritDoc}
1347      */
1348     @Override
1349     public void onTextChanged(CharSequence s, int start, int before, int count) {
1350         this.mEditStart = start;
1351         this.mEditEnd = start + count;
1352     }
1353
1354     /**
1355      * {@inheritDoc}
1356      */
1357     @Override
1358     public void afterTextChanged(Editable s) {
1359         setDirty(true);
1360         if (this.mSyntaxHighlightProcessor != null) {
1361             this.mSyntaxHighlightProcessor.process(s, this.mEditStart, this.mEditEnd);
1362         }
1363     }
1364
1365     /**
1366      * Method that sets if the editor is dirty (has changed)
1367      *
1368      * @param dirty If the editor is dirty
1369      * @hide
1370      */
1371     void setDirty(boolean dirty) {
1372         this.mDirty = dirty;
1373         this.mSave.setVisibility(dirty ? View.VISIBLE : View.GONE);
1374     }
1375
1376     /**
1377      * Check the dirty state of the editor, and ask the user to save the changes
1378      * prior to exit.
1379      */
1380     public void checkDirtyState() {
1381         if (this.mDirty) {
1382             AlertDialog dlg = DialogHelper.createYesNoDialog(
1383                     this,
1384                     R.string.editor_dirty_ask_title,
1385                     R.string.editor_dirty_ask_msg,
1386                     new OnClickListener() {
1387                         @Override
1388                         public void onClick(DialogInterface dialog, int which) {
1389                             if (which == DialogInterface.BUTTON_POSITIVE) {
1390                                 dialog.dismiss();
1391                                 setResult(Activity.RESULT_OK);
1392                                 finish();
1393                             }
1394                         }
1395                     });
1396             DialogHelper.delegateDialogShow(this, dlg);
1397             return;
1398         }
1399         setResult(Activity.RESULT_OK);
1400         finish();
1401     }
1402
1403     /**
1404      * Method that check if a character is valid printable character
1405      *
1406      * @param c The character to check
1407      * @return boolean If the character is printable
1408      * @hide
1409      */
1410     static boolean isPrintableCharacter(char c) {
1411         int cc = VALID_NON_PRINTABLE_CHARS.length;
1412         for (int i = 0; i < cc; i++) {
1413             if (c == VALID_NON_PRINTABLE_CHARS[i]) {
1414                 return true;
1415             }
1416         }
1417         return TextUtils.isGraphic(c);
1418     }
1419
1420     /**
1421      * Method that applies the current theme to the activity
1422      * @hide
1423      */
1424     void applyTheme() {
1425         Theme theme = ThemeManager.getCurrentTheme(this);
1426         theme.setBaseTheme(this, false);
1427
1428         //- ActionBar
1429         theme.setTitlebarDrawable(this, getActionBar(), "titlebar_drawable"); //$NON-NLS-1$
1430         View v = getActionBar().getCustomView().findViewById(R.id.customtitle_title);
1431         theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
1432         v = findViewById(R.id.ab_button1);
1433         theme.setImageDrawable(this, (ImageView)v, "ab_save_drawable"); //$NON-NLS-1$
1434         //- View
1435         v = findViewById(R.id.editor_layout);
1436         theme.setBackgroundDrawable(this, v, "background_drawable"); //$NON-NLS-1$
1437         v = findViewById(R.id.editor);
1438         theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
1439         //- ProgressBar
1440         Drawable dw = theme.getDrawable(this, "horizontal_progress_bar"); //$NON-NLS-1$
1441         this.mProgressBar.setProgressDrawable(dw);
1442
1443         // Need a full process of syntax highlight
1444         if (!this.mBinary && this.mSyntaxHighlight && this.mSyntaxHighlightProcessor != null) {
1445             reloadSyntaxHighlight();
1446         }
1447     }
1448
1449 }