textfile_gui.cpp

Go to the documentation of this file.
00001 /* $Id: textfile_gui.cpp 25925 2013-10-28 12:15:44Z frosch $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "fileio_func.h"
00014 #include "fontcache.h"
00015 #include "gfx_type.h"
00016 #include "gfx_func.h"
00017 #include "string_func.h"
00018 #include "textfile_gui.h"
00019 
00020 #include "widgets/misc_widget.h"
00021 
00022 #include "table/strings.h"
00023 
00025 static const NWidgetPart _nested_textfile_widgets[] = {
00026   NWidget(NWID_HORIZONTAL),
00027     NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
00028     NWidget(WWT_CAPTION, COLOUR_MAUVE, WID_TF_CAPTION), SetDataTip(STR_NULL, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00029     NWidget(WWT_TEXTBTN, COLOUR_MAUVE, WID_TF_WRAPTEXT), SetDataTip(STR_TEXTFILE_WRAP_TEXT, STR_TEXTFILE_WRAP_TEXT_TOOLTIP),
00030     NWidget(WWT_DEFSIZEBOX, COLOUR_MAUVE),
00031   EndContainer(),
00032   NWidget(NWID_HORIZONTAL),
00033     NWidget(WWT_PANEL, COLOUR_MAUVE, WID_TF_BACKGROUND), SetMinimalSize(200, 125), SetResize(1, 12), SetScrollbar(WID_TF_VSCROLLBAR),
00034     EndContainer(),
00035     NWidget(NWID_VERTICAL),
00036       NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_TF_VSCROLLBAR),
00037     EndContainer(),
00038   EndContainer(),
00039   NWidget(NWID_HORIZONTAL),
00040     NWidget(NWID_HSCROLLBAR, COLOUR_MAUVE, WID_TF_HSCROLLBAR),
00041     NWidget(WWT_RESIZEBOX, COLOUR_MAUVE),
00042   EndContainer(),
00043 };
00044 
00046 static WindowDesc _textfile_desc(
00047   WDP_CENTER, "textfile", 630, 460,
00048   WC_TEXTFILE, WC_NONE,
00049   0,
00050   _nested_textfile_widgets, lengthof(_nested_textfile_widgets)
00051 );
00052 
00053 TextfileWindow::TextfileWindow(TextfileType file_type) : Window(&_textfile_desc), file_type(file_type)
00054 {
00055   this->CreateNestedTree();
00056   this->vscroll = this->GetScrollbar(WID_TF_VSCROLLBAR);
00057   this->hscroll = this->GetScrollbar(WID_TF_HSCROLLBAR);
00058   this->FinishInitNested();
00059   this->GetWidget<NWidgetCore>(WID_TF_CAPTION)->SetDataTip(STR_TEXTFILE_README_CAPTION + file_type, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
00060 
00061   this->hscroll->SetStepSize(10); // Speed up horizontal scrollbar
00062   this->vscroll->SetStepSize(FONT_HEIGHT_MONO);
00063 }
00064 
00065 /* virtual */ TextfileWindow::~TextfileWindow()
00066 {
00067   free(this->text);
00068 }
00069 
00074 uint TextfileWindow::GetContentHeight()
00075 {
00076   int max_width = this->GetWidget<NWidgetCore>(WID_TF_BACKGROUND)->current_x - WD_FRAMETEXT_LEFT - WD_FRAMERECT_RIGHT;
00077 
00078   uint height = 0;
00079   for (uint i = 0; i < this->lines.Length(); i++) {
00080     height += GetStringHeight(this->lines[i], max_width, FS_MONO);
00081   }
00082 
00083   return height;
00084 }
00085 
00086 /* virtual */ void TextfileWindow::UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00087 {
00088   switch (widget) {
00089     case WID_TF_BACKGROUND:
00090       resize->height = 1;
00091 
00092       size->height = 4 * resize->height + TOP_SPACING + BOTTOM_SPACING; // At least 4 lines are visible.
00093       size->width = max(200u, size->width); // At least 200 pixels wide.
00094       break;
00095   }
00096 }
00097 
00099 void TextfileWindow::SetupScrollbars()
00100 {
00101   if (IsWidgetLowered(WID_TF_WRAPTEXT)) {
00102     this->vscroll->SetCount(this->GetContentHeight());
00103     this->hscroll->SetCount(0);
00104   } else {
00105     uint max_length = 0;
00106     for (uint i = 0; i < this->lines.Length(); i++) {
00107       max_length = max(max_length, GetStringBoundingBox(this->lines[i], FS_MONO).width);
00108     }
00109     this->vscroll->SetCount(this->lines.Length() * FONT_HEIGHT_MONO);
00110     this->hscroll->SetCount(max_length + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT);
00111   }
00112 
00113   this->SetWidgetDisabledState(WID_TF_HSCROLLBAR, IsWidgetLowered(WID_TF_WRAPTEXT));
00114 }
00115 
00116 /* virtual */ void TextfileWindow::OnClick(Point pt, int widget, int click_count)
00117 {
00118   switch (widget) {
00119     case WID_TF_WRAPTEXT:
00120       this->ToggleWidgetLoweredState(WID_TF_WRAPTEXT);
00121       this->SetupScrollbars();
00122       this->InvalidateData();
00123       break;
00124   }
00125 }
00126 
00127 /* virtual */ void TextfileWindow::DrawWidget(const Rect &r, int widget) const
00128 {
00129   if (widget != WID_TF_BACKGROUND) return;
00130 
00131   const int x = r.left + WD_FRAMETEXT_LEFT;
00132   const int y = r.top + WD_FRAMETEXT_TOP;
00133   const int right = r.right - WD_FRAMETEXT_RIGHT;
00134   const int bottom = r.bottom - WD_FRAMETEXT_BOTTOM;
00135 
00136   DrawPixelInfo new_dpi;
00137   if (!FillDrawPixelInfo(&new_dpi, x, y, right - x + 1, bottom - y + 1)) return;
00138   DrawPixelInfo *old_dpi = _cur_dpi;
00139   _cur_dpi = &new_dpi;
00140 
00141   /* Draw content (now coordinates given to DrawString* are local to the new clipping region). */
00142   int line_height = FONT_HEIGHT_MONO;
00143   int y_offset = -this->vscroll->GetPosition();
00144 
00145   for (uint i = 0; i < this->lines.Length(); i++) {
00146     if (IsWidgetLowered(WID_TF_WRAPTEXT)) {
00147       y_offset = DrawStringMultiLine(0, right - x, y_offset, bottom - y, this->lines[i], TC_WHITE, SA_TOP | SA_LEFT, false, FS_MONO);
00148     } else {
00149       DrawString(-this->hscroll->GetPosition(), right - x, y_offset, this->lines[i], TC_WHITE, SA_TOP | SA_LEFT, false, FS_MONO);
00150       y_offset += line_height; // margin to previous element
00151     }
00152   }
00153 
00154   _cur_dpi = old_dpi;
00155 }
00156 
00157 /* virtual */ void TextfileWindow::OnResize()
00158 {
00159   this->vscroll->SetCapacityFromWidget(this, WID_TF_BACKGROUND, TOP_SPACING + BOTTOM_SPACING);
00160   this->hscroll->SetCapacityFromWidget(this, WID_TF_BACKGROUND);
00161 
00162   this->SetupScrollbars();
00163 }
00164 
00165 /* virtual */ void TextfileWindow::Reset()
00166 {
00167   this->search_iterator = 0;
00168 }
00169 
00170 /* virtual */ FontSize TextfileWindow::DefaultSize()
00171 {
00172   return FS_MONO;
00173 }
00174 
00175 /* virtual */ const char *TextfileWindow::NextString()
00176 {
00177   if (this->search_iterator >= this->lines.Length()) return NULL;
00178 
00179   return this->lines[this->search_iterator++];
00180 }
00181 
00182 /* virtual */ bool TextfileWindow::Monospace()
00183 {
00184   return true;
00185 }
00186 
00187 /* virtual */ void TextfileWindow::SetFontNames(FreeTypeSettings *settings, const char *font_name)
00188 {
00189 #ifdef WITH_FREETYPE
00190   strecpy(settings->mono.font, font_name, lastof(settings->mono.font));
00191 #endif /* WITH_FREETYPE */
00192 }
00193 
00197 /* virtual */ void TextfileWindow::LoadTextfile(const char *textfile, Subdirectory dir)
00198 {
00199   if (textfile == NULL) return;
00200 
00201   this->lines.Clear();
00202 
00203   /* Get text from file */
00204   size_t filesize;
00205   FILE *handle = FioFOpenFile(textfile, "rb", dir, &filesize);
00206   if (handle == NULL) return;
00207 
00208   this->text = ReallocT(this->text, filesize + 1);
00209   size_t read = fread(this->text, 1, filesize, handle);
00210   fclose(handle);
00211 
00212   if (read != filesize) return;
00213 
00214   this->text[filesize] = '\0';
00215 
00216   /* Replace tabs and line feeds with a space since str_validate removes those. */
00217   for (char *p = this->text; *p != '\0'; p++) {
00218     if (*p == '\t' || *p == '\r') *p = ' ';
00219   }
00220 
00221   /* Check for the byte-order-mark, and skip it if needed. */
00222   char *p = this->text + (strncmp("\xEF\xBB\xBF", this->text, 3) == 0 ? 3 : 0);
00223 
00224   /* Make sure the string is a valid UTF-8 sequence. */
00225   str_validate(p, this->text + filesize, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE);
00226 
00227   /* Split the string on newlines. */
00228   *this->lines.Append() = p;
00229   for (; *p != '\0'; p++) {
00230     if (*p == '\n') {
00231       *p = '\0';
00232       *this->lines.Append() = p + 1;
00233     }
00234   }
00235 
00236   CheckForMissingGlyphs(true, this);
00237 }
00238 
00246 const char *GetTextfile(TextfileType type, Subdirectory dir, const char *filename)
00247 {
00248   static const char * const prefixes[] = {
00249     "readme",
00250     "changelog",
00251     "license",
00252   };
00253   assert_compile(lengthof(prefixes) == TFT_END);
00254 
00255   const char *prefix = prefixes[type];
00256 
00257   if (filename == NULL) return NULL;
00258 
00259   static char file_path[MAX_PATH];
00260   strecpy(file_path, filename, lastof(file_path));
00261 
00262   char *slash = strrchr(file_path, PATHSEPCHAR);
00263   if (slash == NULL) return NULL;
00264 
00265   seprintf(slash + 1, lastof(file_path), "%s_%s.txt", prefix, GetCurrentLanguageIsoCode());
00266   if (FioCheckFileExists(file_path, dir)) return file_path;
00267 
00268   seprintf(slash + 1, lastof(file_path), "%s_%.2s.txt", prefix, GetCurrentLanguageIsoCode());
00269   if (FioCheckFileExists(file_path, dir)) return file_path;
00270 
00271   seprintf(slash + 1, lastof(file_path), "%s.txt", prefix);
00272   return FioCheckFileExists(file_path, dir) ? file_path : NULL;
00273 }