diff --git a/programs/cmm/textreader/Tupfile.lua b/programs/cmm/textreader/Tupfile.lua
new file mode 100644
index 0000000000..263f8d756f
--- /dev/null
+++ b/programs/cmm/textreader/Tupfile.lua
@@ -0,0 +1,6 @@
+if tup.getconfig("NO_CMM") ~= "" then return end
+if tup.getconfig("LANG") == "ru"
+then C_LANG = "LANG_RUS"
+else C_LANG = "LANG_ENG" -- this includes default case without config
+end
+tup.rule("textreader.c", "c-- /D=AUTOBUILD /D=$(C_LANG) %f" .. tup.getconfig("KPACK_CMD"), "textreader.com")
diff --git a/programs/cmm/textreader/compile.bat b/programs/cmm/textreader/compile.bat
new file mode 100644
index 0000000000..c8d4886acb
--- /dev/null
+++ b/programs/cmm/textreader/compile.bat
@@ -0,0 +1,6 @@
+C-- "textreader.c"
+@del "textreader"
+@rename "textreader.com" "textreader"
+@del warning.txt
+@pause
+rem kpack textreader
\ No newline at end of file
diff --git a/programs/cmm/textreader/ini.h b/programs/cmm/textreader/ini.h
new file mode 100644
index 0000000000..3df98a799c
--- /dev/null
+++ b/programs/cmm/textreader/ini.h
@@ -0,0 +1,24 @@
+char ini_path[4096];
+char config_section[] = "Config";
+int encoding;
+
+void LoadIniSettings()
+{
+ strcpy(#ini_path, "/sys/settings/treader.ini");
+ ini_get_int stdcall (#ini_path, #config_section, "FontSize", 14); font.size.text = EAX;
+ ini_get_int stdcall (#ini_path, #config_section, "Encoding", CH_CP866); encoding = EAX;
+ ini_get_int stdcall (#ini_path, #config_section, "WinX", 150); Form.left = EAX;
+ ini_get_int stdcall (#ini_path, #config_section, "WinY", 50); Form.top = EAX;
+ ini_get_int stdcall (#ini_path, #config_section, "WinW", 640); Form.width = EAX;
+ ini_get_int stdcall (#ini_path, #config_section, "WinH", 560); Form.height = EAX;
+}
+
+void SaveIniSettings()
+{
+ ini_set_int stdcall (#ini_path, #config_section, "FontSize", font.size.text);
+ ini_set_int stdcall (#ini_path, #config_section, "Encoding", encoding);
+ ini_set_int stdcall (#ini_path, #config_section, "WinX", Form.left);
+ ini_set_int stdcall (#ini_path, #config_section, "WinY", Form.top);
+ ini_set_int stdcall (#ini_path, #config_section, "WinW", Form.width);
+ ini_set_int stdcall (#ini_path, #config_section, "WinH", Form.height);
+}
\ No newline at end of file
diff --git a/programs/cmm/textreader/menu.h b/programs/cmm/textreader/menu.h
new file mode 100644
index 0000000000..5477feaedb
--- /dev/null
+++ b/programs/cmm/textreader/menu.h
@@ -0,0 +1,63 @@
+char stak[4096];
+
+byte action_buf;
+
+llist menu;
+
+void menu_rmb()
+{
+ proc_info MenuForm;
+ menu.ClearList();
+ while (charsets[menu.count]) menu.count++;
+ menu.SetSizes(2,2,140,menu.count*19,19);
+ SetEventMask(100111b);
+ _BEGIN_APPLICATION_MENU:
+ switch(WaitEvent())
+ {
+ case evMouse:
+ GetProcessInfo(#MenuForm, SelfInfo);
+ if (!CheckActiveProcess(MenuForm.ID)) ExitProcess();
+ mouse.get();
+ if (menu.ProcessMouse(mouse.x, mouse.y)) DrawMenuList();
+ if (mouse.lkm)&&(mouse.up) ItemClick();
+ break;
+ case evKey:
+ GetKeys();
+ if (key_scancode==SCAN_CODE_ESC) ExitProcess();
+ if (key_scancode==SCAN_CODE_ENTER) ItemClick();
+ if (menu.ProcessKey(key_scancode)) DrawMenuList();
+ break;
+ case evReDraw:
+ DefineAndDrawWindow(Form.left+104,Form.top+29+SKIN.height,menu.w+2,menu.h+4,0x01, 0, 0, 0x01fffFFF);
+ DrawPopup(0,0,menu.w,menu.h+3,0, 0xE4DFE1,0x9098B0);
+ DrawMenuList();
+ }
+ goto _BEGIN_APPLICATION_MENU;
+}
+
+void DrawMenuList()
+{
+ int N;
+ for (N=0; N
2) return;
+ if (Form.width < 200) { MoveSize(OLD,OLD,200,OLD); return; }
+ if (Form.height < 200) { MoveSize(OLD,OLD,OLD,200); return; }
+ DrawBar(0, 0, Form.cwidth, TOOLBAR_H - 1, 0xe1e1e1);
+ DrawBar(0, TOOLBAR_H - 1, Form.cwidth, 1, 0x7F7F7F);
+ DrawToolbarButton(OPEN_FILE, 8);
+ DrawToolbarButton(MAGNIFY_PLUS, 42);
+ DrawToolbarButton(MAGNIFY_MINUS, 67);
+ DrawToolbarButton(CHANGE_ENCODING, 101);
+ DrawToolbarButton(RUN_EDIT, 135);
+ DrawToolbarButton(SHOW_INFO, Form.cwidth - 34);
+ if (Form.cwidth-scroll.size_x-1 == list.w) && (Form.cheight-TOOLBAR_H == list.h) && (list.count) DrawPage(); else PreparePage();
+ DrawRectangle(scroll.start_x, scroll.start_y, scroll.size_x, scroll.size_y-1, scroll.bckg_col);
+}
+
+void DrawPage()
+{
+ _PutImage(list.x,list.y,list.w,list.h,list.first*list.line_h*list.w*3 + font.buffer);
+ DrawScroller();
+}
+
+void PreparePage()
+{
+ char line[4096]=0;
+ dword line_start;
+ byte ch;
+ dword bufoff;
+ dword line_length=30;
+ dword stroka_y = 5;
+ dword stroka=0;
+ char ch_width[255];
+ int i, srch_pos;
+ font.changeSIZE();
+ list.w = Form.cwidth-scroll.size_x-1;
+ //get font chars width, need to increase performance
+ for (i=0; i<256; i++) ch_width[i] = font.symbol_size(i);
+ //get font buffer height
+ for (bufoff=io.buffer_data; ESBYTE[bufoff]; bufoff++)
+ {
+ ch = ESBYTE[bufoff];
+ line_length += ch_width[ch];
+ if (line_length>=list.w) || (ch==10) {
+ srch_pos = bufoff;
+ loop()
+ {
+ if (__isWhite(ESBYTE[srch_pos])) { bufoff=srch_pos+1; break; } //normal word-break
+ if (srch_pos == line_start) break; //no white space found in whole line
+ srch_pos--;
+ }
+ line_start = bufoff;
+ line_length = 30;
+ stroka++;
+ }
+ }
+ //draw text in buffer
+ list.count = stroka+2;
+ list.SetSizes(0, TOOLBAR_H, list.w, Form.cheight-TOOLBAR_H, font.size.text+1);
+ if (list.count < list.visible) list.count = list.visible;
+
+ font.size.height = list.count+1*list.line_h;
+ font.buffer_size = 0;
+
+ line_length = 30;
+ line_start = io.buffer_data;
+ for (bufoff=io.buffer_data; ESBYTE[bufoff]; bufoff++)
+ {
+ ch = ESBYTE[bufoff];
+ line_length += ch_width[ch];
+ if (line_length>=list.w) || (ch==10)
+ {
+ //set word break
+ srch_pos = bufoff;
+ loop()
+ {
+ if (__isWhite(ESBYTE[srch_pos])) { bufoff=srch_pos+1; break; } //normal word-break
+ if (srch_pos == line_start) break; //no white space found in whole line
+ srch_pos--;
+ }
+ i = bufoff-line_start;
+ strlcpy(#line, line_start, i);
+ font.prepare_buf(8,stroka_y,list.w,font.size.height, #line);
+ stroka_y += list.line_h;
+ line_start = bufoff;
+ line_length = 30;
+ }
+ }
+ font.prepare_buf(8,stroka_y,list.w,font.size.height, line_start);
+ SmoothFont(font.buffer, font.size.width, font.size.height);
+ DrawPage();
+}
+
+void DrawToolbarButton(char image_id, int x)
+{
+ DefineButton(x, 5, 26-1, 24-1, 10+image_id + BT_HIDE, 0);
+ img_draw stdcall(skin.image, x, 5, 26, 24, 0, image_id*24);
+}
+
+void DrawScroller()
+{
+ scroll.max_area = list.count;
+ scroll.cur_area = list.visible;
+ scroll.position = list.first;
+ scroll.all_redraw = 0;
+ scroll.start_x = list.x + list.w;
+ scroll.start_y = list.y;
+ scroll.size_y = list.h;
+ scroll.start_x = list.x + list.w;
+ scrollbar_v_draw(#scroll);
+}
+
+void OpenFile(dword f_path)
+{
+ int tmp;
+ if (ESBYTE[f_path]) {
+ strcpy(#param, f_path);
+ io.read(#param);
+ strcpy(#title, #param);
+ strcat(#title, " - Text Reader");
+ }
+ else {
+ if (list.count) return;
+ io.buffer_data = "This is a plain text reader.\nTry to open some text file.";
+ strcpy(#title, "Text Reader");
+ }
+ if (encoding!=CH_CP866) ChangeCharset(charsets[encoding], "CP866", io.buffer_data);
+ list.KeyHome();
+ list.ClearList();
+}
+
+
+
+char *about[] = {
+ "Text Reader v1.0",
+ "Idea: Leency, punk_joker",
+ "Code: Leency, KolibriOS Team",
+ " ",
+ "Hotkeys:",
+ "Ctrl+O - open file",
+ "Ctrl+Up - bigger font",
+ "Ctrl+Down - smaller font",
+ "Ctrl+Tab - select charset",
+ "Ctrl+E - edit current document",
+ " ",
+ "Press any key...",
+ 0
+};
+
+ShowAbout() {
+ int i;
+ help_opened = true;
+ DrawBar(list.x, list.y, list.w, list.h, 0xFFFfff);
+ WriteText(list.x + 10, list.y + 10, 10000001b, 0x555555, about[0]);
+ for (i=1; about[i]; i++) WriteText(list.x + 10, i+1*20 + list.y, 10110000b, 0, about[i]);
+}
\ No newline at end of file
diff --git a/programs/cmm/textreader/toolbar.png b/programs/cmm/textreader/toolbar.png
new file mode 100644
index 0000000000..23287859b0
Binary files /dev/null and b/programs/cmm/textreader/toolbar.png differ