Calculadora básica con interfaz [Win API][Terminada]

Iniciado por Alien-Z, 17 Septiembre 2011, 14:35 PM

0 Miembros y 1 Visitante están viendo este tema.

Alien-Z

Bueno, finalmente he acabado una calculadora muy sencilla con interfaz poniendo en práctica lo que he aprendido de la API de Windows; la cuelgo aqui por si le sirve a alguien de ejemplo (todas las funciones y códigos van comentados).

main.cpp:

Código (cpp) [Seleccionar]
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <math.h>

//ID´s
enum {ID_LABEL, ID_EDIT, ID_BOTONIGUAL, ID_BOTONRESET, ID_BOTONRESET2, ID_BOTONSUMAR, ID_BOTONRESTAR, ID_BOTONMULTIPLICAR, ID_BOTONDIVIDIR, ID_BOTONPORCENTAJE, ID_BOTONPOTENCIA, ID_BOTONRAIZ, ID_BOTONPI, ID_BOTONCOMA, ID_BOTONSIGNO, ID_BOTONCERO, ID_BOTON1, ID_BOTON2, ID_BOTON3, ID_BOTON4, ID_BOTON5, ID_BOTON6, ID_BOTON7, ID_BOTON8, ID_BOTON9};


//Prototipos
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

void imprimirEdit (int i);                                                  //Funcion que concatena los numeros al hacer clic en los botones
double calcular (double primer_num, double segundo_num, int operacion);     //Funcion que realiza las operaciones

HINSTANCE estancia;
HWND label;
HWND edit;
//Iconos
HICON iconoPotencia;
HICON iconoRaiz;
HICON iconoPi;
//Botones
HWND boton1;
HWND boton2;
HWND boton3;
HWND boton4;
HWND boton5;
HWND boton6;
HWND boton7;
HWND boton8;
HWND boton9;
HWND boton0;
HWND botonComa;
HWND botonSigno;
HWND botonIgual;
HWND botonReset;
HWND botonReset2;
HWND botonSumar;
HWND botonRestar;
HWND botonMultiplicar;
HWND botonDividir;
HWND botonPorcentaje;
HWND botonPotencia;
HWND botonRaiz;
HWND botonPi;


char szClassName[ ] = "CodeBlocksWindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nCmdShow)
{
   HWND hwnd;               /* This is the handle for our window */
   MSG messages;            /* Here messages to the application are saved */
   WNDCLASSEX wincl;        /* Data structure for the windowclass */

   estancia = hThisInstance;

   /* The Window structure */
   wincl.hInstance = hThisInstance;
   wincl.lpszClassName = szClassName;
   wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
   wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
   wincl.cbSize = sizeof (WNDCLASSEX);

   /* Use default icon and mouse-pointer */
   wincl.hIcon = LoadIcon (estancia, "ICONO");
   wincl.hIconSm = LoadIcon (estancia, "ICONO");
   wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
   wincl.lpszMenuName = NULL;                 /* No menu */
   wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
   wincl.cbWndExtra = 0;                      /* structure or the window instance */

   //Cargamos los iconos de los botones
   iconoPotencia = LoadIcon (estancia, "POTENCIA");
   iconoRaiz = LoadIcon (estancia, "RAIZ");
   iconoPi = LoadIcon (estancia, "PI");

   //Color de fondo de la aplicacion
   wincl.hbrBackground = (HBRUSH) CreateSolidBrush (RGB(88, 145, 192));

   if (!RegisterClassEx (&wincl))
       return 0;

   hwnd = CreateWindowEx (
          0,                   /* Extended possibilites for variation */
          szClassName,         /* Classname */
          "Calculadora by Alien-Z",
          WS_OVERLAPPEDWINDOW, /* default window */
          CW_USEDEFAULT,       /* Windows decides the position */
          CW_USEDEFAULT,       /* where the window ends up on the screen */
          300,                 /* The programs width */
          300,                 /* and height in pixels */
          HWND_DESKTOP,        /* The window is a child-window to desktop */
          NULL,                /* No menu */
          hThisInstance,       /* Program Instance handler */
          NULL                 /* No Window Creation data */
          );

   /* Make the window visible on the screen */
   ShowWindow (hwnd, nCmdShow);

   /* Run the message loop. It will run until GetMessage() returns 0 */
   while (GetMessage (&messages, NULL, 0, 0))
   {
       /* Translate virtual-key messages into character messages */
       TranslateMessage(&messages);
       /* Send message to WindowProcedure */
       DispatchMessage(&messages);
   }

   /* The program return-value is 0 - The value that PostQuitMessage() gave */
   return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   PAINTSTRUCT ps;
   HDC hdc;
   TCHAR greeting[] = _T("Alien-Z");
   COLORREF color1 = RGB (192, 192, 192);

   //Variables
   char cadenaEditMain [31];
   static int operacion;
   static double primer_num, segundo_num, resultado, signo;


   switch (message)
   {
       case WM_CREATE:
       {
           //Elementos de la calculadora
           label = CreateWindow ("Static", NULL, BS_CENTER | WS_CHILD | WS_VISIBLE, 20, 20, 250, 25, hwnd, 0, estancia, 0);
           edit = CreateWindowEx (WS_EX_CLIENTEDGE, "edit", 0, ES_RIGHT | ES_NUMBER | WS_BORDER | WS_CHILD | WS_VISIBLE, 20, 50, 250, 25, hwnd, (HMENU)ID_EDIT, estancia, 0);

           boton1 = CreateWindow ("Button", "1", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 20, 90, 40, 25, hwnd, (HMENU) ID_BOTON1, estancia, 0);
           boton2 = CreateWindow ("Button", "2", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 65, 90, 40, 25, hwnd, (HMENU) ID_BOTON2, estancia, 0);
           boton3 = CreateWindow ("Button", "3", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 90, 40, 25, hwnd, (HMENU) ID_BOTON3, estancia, 0);
           boton4 = CreateWindow ("Button", "4", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 20, 120, 40, 25, hwnd, (HMENU) ID_BOTON4, estancia, 0);
           boton5 = CreateWindow ("Button", "5", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 65, 120, 40, 25, hwnd, (HMENU) ID_BOTON5, estancia, 0);
           boton6 = CreateWindow ("Button", "6", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 120, 40, 25, hwnd, (HMENU) ID_BOTON6, estancia, 0);
           boton7 = CreateWindow ("Button", "7", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 20, 150, 40, 25, hwnd, (HMENU) ID_BOTON7, estancia, 0);
           boton8 = CreateWindow ("Button", "8", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 65, 150, 40, 25, hwnd, (HMENU) ID_BOTON8, estancia, 0);
           boton9 = CreateWindow ("Button", "9", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 150, 40, 25, hwnd, (HMENU) ID_BOTON9, estancia, 0);
           boton0 = CreateWindow ("Button", "0", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 20, 180, 40, 25, hwnd, (HMENU) ID_BOTONCERO, estancia, 0);
           botonComa = CreateWindow ("Button", ",", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 65, 180, 40, 25, hwnd, (HMENU) ID_BOTONCOMA, estancia, 0);
           botonSigno = CreateWindow ("Button", "+/-", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 180, 40, 25, hwnd, (HMENU) ID_BOTONSIGNO, estancia, 0);
           botonSumar = CreateWindow ("Button", "+", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 165, 90, 40, 25, hwnd, (HMENU) ID_BOTONSUMAR, estancia, 0);
           botonRestar = CreateWindow ("Button", "-", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 165, 120, 40, 25, hwnd, (HMENU) ID_BOTONRESTAR, estancia, 0);
           botonMultiplicar = CreateWindow ("Button", "*", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 165, 150, 40, 25, hwnd, (HMENU) ID_BOTONMULTIPLICAR, estancia, 0);
           botonDividir = CreateWindow ("Button", "/", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 165, 180, 40, 25, hwnd, (HMENU) ID_BOTONDIVIDIR, estancia, 0);
           botonPorcentaje = CreateWindow ("Button", "%", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 210, 90, 40, 25, hwnd, (HMENU) ID_BOTONPORCENTAJE, estancia, 0);
           botonPotencia = CreateWindow ("Button", "x ²", BS_ICON | BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 210, 120, 40, 25, hwnd, (HMENU) ID_BOTONPOTENCIA, estancia, 0);
           botonRaiz = CreateWindow ("Button", "Raiz", BS_ICON | BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 210, 150, 40, 25, hwnd, (HMENU) ID_BOTONRAIZ, estancia, 0);
           botonPi = CreateWindow ("Button", "Pi", BS_ICON | BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 210, 180, 40, 25, hwnd, (HMENU) ID_BOTONPI, estancia, 0);
           botonIgual = CreateWindow ("Button", "=", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 20, 220, 40, 25, hwnd, (HMENU) ID_BOTONIGUAL, estancia, 0);
           botonReset = CreateWindow ("Button", "C", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 65, 220, 40, 25, hwnd, (HMENU) ID_BOTONRESET, estancia, 0);
           botonReset2 = CreateWindow ("Button", "CE", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 220, 40, 25, hwnd, (HMENU) ID_BOTONRESET2, estancia, 0);

           //Cambiar fuente de letra del lablel
           static HFONT hFont = CreateFont (16, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, VARIABLE_PITCH | FF_SWISS, "Comic Sans MS");
           SendMessage(label, WM_SETFONT, (WPARAM) hFont, true);

           //Enviar iconos a los botones
           SendMessage(botonPotencia, BM_SETIMAGE, (WPARAM) IMAGE_ICON, (LPARAM) iconoPotencia);
           SendMessage(botonRaiz, BM_SETIMAGE, (WPARAM) IMAGE_ICON, (LPARAM) iconoRaiz);
           SendMessage(botonPi, BM_SETIMAGE, (WPARAM) IMAGE_ICON, (LPARAM) iconoPi);

           //Bloquear el boton "maximizar"
           DWORD dwStyle = (DWORD)GetWindowLong(hwnd, GWL_STYLE);
           dwStyle &= ~WS_MAXIMIZEBOX;
           SetWindowLong(hwnd, GWL_STYLE, (DWORD)dwStyle);
           RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW);

           //Bloquear la opcion de cambiarle el tamaño a la ventana
           dwStyle &= ~WS_SIZEBOX;
           SetWindowLong(hwnd, GWL_STYLE, (DWORD)dwStyle);
           RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW);
       }

       case WM_COMMAND:
       {
           switch (wParam)
           {
               case ID_BOTON1:                  //Al presionar el boton 1 se llama a la funcion "imprimirEdit" y se le envia el valor "1" que es lo que queremos que se concatene a lo que ya haya en el edit
               {
                   imprimirEdit (1);
                   break;
               }
               case ID_BOTON2:
               {
                   imprimirEdit (2);
                   break;
               }
               case ID_BOTON3:
               {
                   imprimirEdit (3);
                   break;
               }
               case ID_BOTON4:
               {
                   imprimirEdit(4);
                   break;
               }
               case ID_BOTON5:
               {
                   imprimirEdit(5);
                   break;
               }
               case ID_BOTON6:
               {
                   imprimirEdit(6);
                   break;
               }
               case ID_BOTON7:
               {
                   imprimirEdit(7);
                   break;
               }
               case ID_BOTON8:
               {
                   imprimirEdit(8);
                   break;
               }
               case ID_BOTON9:
               {
                   imprimirEdit(9);
                   break;
               }
               case ID_BOTONCERO:
               {
                   imprimirEdit(0);
                   break;
               }
               case ID_BOTONCOMA:
               {
                   GetWindowText (edit, cadenaEditMain, 30);        //Se obtiene lo que hay en el edit y se guarda en "CadenaEditMain"
                   if (strstr (cadenaEditMain, ".") == NULL)        //Analizamos "cadenaEditMain" y si no posee una coma se ejecuta lo que hay dentro del if (en caso de que ya tenga una coma no hace nada)
                   {
                       strcat (cadenaEditMain, ".");                //Se le añade una coma a lo que hay en "CadenaEditMain"
                       SetWindowText (edit, cadenaEditMain);        //Se imprime el nuevo numero en el edit
                   }
                   break;
               }
               case ID_BOTONSIGNO:                                  //Boton que cambia el signo del numero
               {
                   GetWindowText (edit, cadenaEditMain, 30);        //Se obtiene lo que hay en el edit y se guarda en "CadenaEditMain"
                   signo = atof (cadenaEditMain);                   //Transforma la cadena obtenida en un double y lo guarda en "signo"
                   signo *= -1;                                     //Se multiplica el numero por -1 (si es positivo se quedara negativo, y viceversa)
                   sprintf (cadenaEditMain, "%f", signo);           //Se transforma el double obtenido en una cadena y se guarda en "cadenaEditMain"
                   SetWindowText (edit, cadenaEditMain);            //Se imprime el nuevo numero en el edit
                   break;
               }
               case ID_BOTONSUMAR:
               {
                   operacion = 1;                                    //Cambia el valor de "operacion" para indicar que se va a sumar
                   GetWindowText (edit, cadenaEditMain, 30);         //Obtiene el numero ingresado en el edit
                   primer_num = atof (cadenaEditMain);               //Transforma la cadena obtenida en un double y lo guarda en "primer_num"
                   SetWindowText (edit, "");                         //Limpia el edit
                   break;
               }
               case ID_BOTONRESTAR:
               {
                   operacion = 2;                                    //Cambia el valor de "operacion" para indicar que se va a restar
                   GetWindowText (edit, cadenaEditMain, 30);
                   primer_num = atof (cadenaEditMain);
                   SetWindowText (edit, "");
                   break;
               }
               case ID_BOTONMULTIPLICAR:
               {
                   operacion = 3;                                    //Cambia el valor de "operacion" para indicar que se va a multiplicar
                   GetWindowText (edit, cadenaEditMain, 30);
                   primer_num = atof (cadenaEditMain);
                   SetWindowText (edit, "");
                   break;
               }
               case ID_BOTONDIVIDIR:
               {
                   operacion = 4;                                    //Cambia el valor de "operacion" para indicar que se va a dividir
                   GetWindowText (edit, cadenaEditMain, 30);
                   primer_num = atof (cadenaEditMain);
                   SetWindowText (edit, "");
                   break;
               }
               case ID_BOTONPORCENTAJE:
               {
                   operacion = 5;                                    //Cambia el valor de "operacion" para indicar que vamos a hallar el porcentaje
                   GetWindowText (edit, cadenaEditMain, 30);
                   primer_num = atof (cadenaEditMain);
                   SetWindowText (edit, "");
                   break;
               }
               case ID_BOTONPOTENCIA:
               {
                   operacion = 6;                                    //Cambia el valor de "operacion" para indicar que vamos a hallar una potencia
                   GetWindowText (edit, cadenaEditMain, 30);
                   primer_num = atof (cadenaEditMain);
                   SetWindowText (edit, "");
                   SetWindowText (label, "¿A cuánto está elevado el número?");
                   break;
               }
               case ID_BOTONRAIZ:
               {
                   operacion = 7;                                    //Cambia el valor de "operacion" para indicar que vamos a hallar la raiz
                   GetWindowText (edit, cadenaEditMain, 30);
                   primer_num = atof (cadenaEditMain);
                   SetWindowText (edit, "");
                   SetWindowText (label, "¿A cuánto está elevada la raiz?");
                   break;
               }
               case ID_BOTONPI:
               {
                   SetWindowText (edit, "3.141592653589793");        //Imprime en el edit el valor de PI
                   break;
               }
               case ID_BOTONIGUAL:
               {
                   GetWindowText (edit, cadenaEditMain, 30);         //Obtiene el numero ingresado en el edit
                   segundo_num = atof (cadenaEditMain);              //Transforma la cadena obtenida en un double y lo guarda en "segundo_num"
                   resultado = calcular (primer_num, segundo_num, operacion); //El resultado se obtiene apartir de la funcion "calcular", a esta funcion se le envian las variables "primer_num", "segundo_num" y "operacion"
                   sprintf (cadenaEditMain, "%f", resultado);        //Se transforma el double obtenido en una cadena y se guarda en "cadenaEditMain"
                   SetWindowText (edit, cadenaEditMain);             //Se imprime en el edit la cadena donde se encuentra el resultado
                   //Reseteamos las variables
                   primer_num = 0;
                   segundo_num = 0;
                   operacion = 0;
                   break;
               }
               case ID_BOTONRESET:                                  //Boton que limpia las variables y datos para calcular de nuevo
               {
                   primer_num = 0;
                   segundo_num = 0;
                   operacion = 0;
                   SetWindowText (edit, "");
                   SetWindowText (label, "");
                   break;
               }
               case ID_BOTONRESET2:                                 //Boton que borra solo el ultimo numero introducido
               {
                   segundo_num = 0;
                   SetWindowText (edit, "");
                   break;
               }

               break;
           }
           break;
       }

       case WM_PAINT:
       {
           hdc = BeginPaint(hwnd, &ps);
           SetBkColor (hdc, RGB(88, 145, 192));
           SetTextColor (hdc, color1);
           TextOut(hdc, 230, 240, greeting, _tcslen(greeting));
           EndPaint (hwnd, &ps);
           break;
       }

       case WM_DESTROY:
           PostQuitMessage (0);
           break;
       default:
           return DefWindowProc (hwnd, message, wParam, lParam);
   }

   return 0;
}

void imprimirEdit (int i)                        //Funcion que concatena los numeros al hacer clic en los botones
{
   char cadenaEdit [31];                        //Variable donde se guardara lo que hay en el edit (no es la misma variable que cadenaEditMain)
   char numero [3];                             //Variable donde se guardara el numero de la tecla presionada
   sprintf (numero, "%i", i);                   //Se transforma el numero (i) del tipo entero a una cadena

   GetWindowText (edit, cadenaEdit, 30);        //Se obtiene lo que hay en el edit y se guarda en "CadenaEdit"
   strcat (cadenaEdit, numero);                 //Se une lo que hay en "CadenaEdit" y lo que hay en "numero" (es decir, se une lo que habia en el edit y el numero de la tecla presionada) y se queda guardada la nueva cifra en "cadenaEdit"
   SetWindowText (edit, cadenaEdit);            //Se imprime el nuevo numero en el edit
}

double calcular (double primer_num, double segundo_num, int operacion)   //Funcion que realiza las operaciones
{
   double resultado;                                                    //Variable donde se guardara el resultado (sera lo que se retorne)

   switch (operacion)                                                   //Ver lo que hay en la variable "operacion"
   {
       case 1:                                                          //En caso de que sea "1" se suma el primer numero al segundo
       {
           resultado = primer_num + segundo_num;
           break;
       }
       case 2:                                                          //En caso de que sea "2" se le resta el segundo numero al primero
       {
           resultado = primer_num - segundo_num;
           break;
       }
       case 3:                                                          //En caso de que sea "3" se multiplica el primer numero por el segundo
       {
           resultado = primer_num * segundo_num;
           break;
       }
       case 4:                                                          //En caso de que sea "4" se divide el primer numero entre el segundo
       {
           resultado = primer_num / segundo_num;
           break;
       }
       case 5:                                                          //En caso de que sea "5" se haya el tanto por cierto (primer numero) del segundo numero
       {
           resultado = (primer_num * segundo_num) / 100;
           break;
       }
       case 6:                                                          //En caso de que sea "6" se obtiene la potencia del primer numero elevado al segundo
       {
           resultado = pow (primer_num, segundo_num);
           break;
       }
       case 7:                                                          //En caso de que sea "7" se obtiene la raiz del primer numero (el segundo define si dicha raiz es cubica, cuadrada...)
       {
           resultado = pow (primer_num, 1/segundo_num);
           break;
       }
       default:                                                         //Si no se realiza ninguna operacion se imprime en el label un mensaje, con esto sabriamos si ha habido algun error
       {
           SetWindowText (label, "No se ha realizado ninguna operacion.");
           break;
       }
   }

   SetWindowText (label, "");                                           //Limpiamos el label
   return resultado;                                                    //Devuelve el resultado
}


recursos.rc:

Código (cpp) [Seleccionar]
ICONO ICON "icono.ico"
POTENCIA ICON "potencia.ico"
RAIZ ICON "raiz.ico"
PI ICON "pi.ico"


Iconos (no he encontrado ningún servidor para subir .ico que acepte fondo transparente asi que he optado por MU): http://www.megaupload.com/?d=ZMPELBPE

Notas:
-Los iconos deben estar dentro de la carpeta del proyecto.
-Aun no he solucionado un pequeño inconveniente y es que solo opera con los dos últimos números introducidos, por ejemplo, si presionas 2+2*5 el resultado será 10 porque la última operación es 2*5, pero si después de cada cálculo presionas el botón igual hará todas las operaciones que le mandes sin problema.
-La función del botón "porcentaje" suele variar según la calculadora, en este caso hayas el X por ciento de Y, por ejemplo, 50 % 10 = 5 (el 50 por ciento de 10 es 5).


Espero que le sirva a alguien. Saludos.

тαптяα

Esta muy bien, para los que empiezan con WINAPI.

Aunque ya te dijeron que mejor usa clases así no tendrás el code tan largo.